我有一个 Greasemonkey 用户脚本,它在任务队列上运行。该队列是一堆序列化的 JSON 对象,分散在脚本私有首选项空间中,可以使用
GM_getValue
、GM_setValue
和 GM_listValues
进行访问。
我想让这个脚本同时运行(同一台机器,同一浏览器,不同的选项卡),同时仍然保持共享队列。我知道 Javascript 主要在单个线程中运行(Web Workers 除外),但我认为在单独的选项卡中打开的页面并非如此,因此它必须同时运行。
在这种情况下,我如何正确组织对此类对象的共享访问,以防止竞争条件?竞争条件会出现吗?
令我自己惊讶的是,似乎没有竞争条件,因为 Javascript 在整个浏览器中单线程运行。我已经使用以下用户脚本对其进行了测试:
// ==UserScript==
// @name test-concurrency
// @namespace mw.tools
// @include /^http://localhost//
// @version 1
// @grant GM_getValue
// @grant GM_setValue
// ==/UserScript==
var BLOCK = 1000;
var ATOM = 'foo';
switch (window.location.search) {
case '?inc':
setInterval(function() {
var i;
for (i = 0; i < BLOCK; i++) {
GM_setValue(ATOM, GM_getValue(ATOM, 0) + 1);
}
document.title = '' + GM_getValue(ATOM, 0) + ' || ' + (GM_getValue(ATOM, 0) % BLOCK);
}, 1);
break;
case '?ver':
setInterval(function() {
var v;
var v0;
v0 = GM_getValue(ATOM);
for (i = 0; i < BLOCK; i++) {
v = GM_getValue(ATOM);
if (v != v0) {
document.title = v0 + ' || broken';
alert('BORKEH!!1111oneone');
}
}
document.title = v0;
}, 1);
break;
}
使用较大的
BLOCK
值时,无论在哪个选项卡或窗口中运行违规脚本,浏览器似乎都没有响应。
因此,对于同步脚本,保持一些共享标志告诉结构已在使用中就足够了,并进行随机间隔轮询以使其变得空闲(简单的方法,但可能会挨饿),或者使用
postWindow()
广播并保持挂起想要获取“互斥体”的队列,实现监视器(某种程度上,因为每个脚本调用都在关键部分中)。