浏览器

时间:2015-10-23 22:56:15

标签: javascript algorithm mutual-exclusion cross-window-scripting

我正在尝试在网页中实现以下功能:

  • 用户可以打开页面的多个标签/窗口。
  • 每隔几秒钟,我需要正好一个这些标签/窗口来执行特定的代码段(关键区域)。
  • 我不关心哪个选项卡/窗口执行代码,即无需担心解决方案的公平性或饥饿性。
  • 由于用户自己打开了标签/窗口,页面的不同实例不知道或直接引用彼此(即没有window.parent等)。
  • 我不想要Flash或Silverlight或其他插件,一切都需要在客户端运行,因此标签/窗口的通信方式非常有限(LocalStorage是我唯一的一个到目前为止发现,但可能还有其他人。)
  • 任何标签/窗口都可以随时崩溃或关闭或刷新,并且可以随时打开更多标签/窗口,其余窗口必须“反应”,这样我仍然可以完全一个每隔几秒执行一次关键区域。
  • 这需要在尽可能多的浏览器中可靠地运行,包括移动(caniuse - 超过90%的评级。)

我首次尝试解决方案是使用一个简单的互斥算法,该算法使用LocalStorage作为共享内存。对于various reasons,我从他们的论文"Mutual Exclusion Using Indivisible Reads and Writes"(第4页(836))中选择了Burns和Lynch的互斥算法。

我构建了一个jsfiddle (see code below)来尝试这个想法,它在Firefox中运行得非常好。如果您想尝试一下,请在Firefox的几个(最多20个)窗口中打开小提琴的链接,并观看其中一个每秒闪烁橙色。如果您同时看到多个眨眼,请告诉我们! :)(注意:我在小提琴中分配ID的方式有点俗气(简单地循环超过0..19),只有每个窗口都分配了不同的ID才会有效。如果两个窗口显示相同的ID,只需重装一个。)。

不幸的是,在Chrome中,特别是在Internet Explorer中,事情无法按计划运行(多个窗口闪烁)。我认为这是由于我将写入LocalStorage的数据从一个选项卡/窗口传播到另一个选项卡/窗口的延迟(请参阅my question about this here)。

所以,基本上,我需要找到一种可以处理延迟数据的不同互斥算法(听起来很难/不可能),或者我需要找到一种完全不同的方法。也许StorageEvents可以提供帮助吗?或者可能有不同的机制不使用LocalStorage?

为了完整性,这里是小提琴的代码:

// Global constants
var LOCK_TIMEOUT =  300; // Locks time out after 300ms
var INTERVAL     = 1000; // Critical section should run every second



//==================================================================================
// Assign process ID

var myID;
id = window.localStorage.getItem("id");

if (id==null) id = 0;
id = Number(id);
myID = id;
id = (id+1) % 20;
window.localStorage.setItem("id", id);

document.documentElement.innerHTML = "ID: "+myID;



//==================================================================================
// Method to indicate critical section

var lastBlink = 0;
function blink() {
    col = Math.round(Math.min((new Date().getTime() - lastBlink)*2/3, 255));
    document.body.style.backgroundColor = "rgb(255, "+((col >> 1)+128)+", "+col+")";
}



//==================================================================================
// Helper methods to implement expiring flags

function flagUp() {
    window.localStorage.setItem("F"+myID, new Date().getTime());
}

function flagDown() {
    window.localStorage.setItem("F"+myID, 0);
}

// Try to refresh flag timeout and return whether we're sure that it never expired
function refreshFlag() {
    content = window.localStorage.getItem("F"+myID);
    if (content==null) return false;
    content = Number(content);
    if ((content==NaN) || (Math.abs(new Date().getTime() - content)>=timeout))
        return false;
    window.localStorage.setItem("F"+myID, new Date().getTime());
    return Math.abs(new Date().getTime() - content) < timeout;
}    

function setFlag(key) {
    window.localStorage.setItem(key, new Date().getTime());
}

function checkFlag(key, timeout) {
    content = window.localStorage.getItem(key);
    if (content==null) return false;
    content = Number(content);
    if (content==NaN) return false;
    return Math.abs(new Date().getTime() - content) < timeout;
}



//==================================================================================
// Burns-Lynch mutual exclusion algorithm

var atLine7 = false;

function enterCriticalRegion() {

    // Refresh flag timeout and restart algorithm if flag may have expired
    if (atLine7) atLine7 &= refreshFlag();

    // Check if run is due
    if (checkFlag("LastRun", INTERVAL)) return false;

    if (!atLine7) {
        // 3: F[i] down
        flagDown();

        // 4: for j:=1 to i-1 do if F[j] = up goto 3
        for (j=0; j<myID; j++)
            if (checkFlag("F"+j, LOCK_TIMEOUT)) return false;

        // 5: F[i] up
        flagUp();

        // 6: for j:=1 to i-1 do if F[j] = up goto 3
        for (j=0; j<myID; j++)
            if (checkFlag("F"+j, LOCK_TIMEOUT)) return false;

        atLine7 = true;
    }

    // 7: for j:=i+1 to N do if F[j] = up goto 7
    for (j=myID+1; j<20; j++)
        if (checkFlag("F"+j, LOCK_TIMEOUT)) return false;

    // Check again if run is due
    return !checkFlag("LastRun", INTERVAL);
}

function leaveCriticalRegion() {
    // Remember time of last succesful run
    setFlag("LastRun");

    // Release lock on critical region
    atLine7 = false;
    window.localStorage.setItem("F"+myID, 0);
}



//==================================================================================
// Keep trying to enter critical region and blink on success

function run() {
    if (enterCriticalRegion()) {
        lastBlink = new Date().getTime();
        leaveCriticalRegion();
    }
}

// Go!
window.setInterval(run,   10);
window.setInterval(blink, 10);

0 个答案:

没有答案