在HTML5本地存储上实现同步原语

3
考虑这样一种情况,浏览器有两个或更多指向同一源的选项卡。不同选项卡的不同事件循环可能会导致在访问本地存储时出现竞争条件,而不同的选项卡可能会潜在地覆盖彼此在本地存储中所做的更改。
我正在编写一个将面临这种竞争条件的Web应用程序,因此我想了解可在此情况下使用的不同同步原语。

1
这是一篇你应该阅读的文章:http://balpha.de/2012/03/javascript-concurrency-and-locking-the-html5-localstorage/ - Pointy
请参阅规范的此部分:http://www.w3.org/TR/webstorage/#threads。根据该规范,允许本地存储以不可预测的方式更改的浏览器是有缺陷的。 - Pointy
@Pointy 我已经读了这篇文章,还有 FastMail 的这篇。就规范而言,我猜那能保证本地存储的独立读写操作是原子性的。即使有这种保证,我提到的竞争条件仍然会发生。 - Bharat Khatri
“本地存储互斥锁”的目的是,一旦执行线程(JavaScript事件循环)获取了互斥锁,它将保持到事件循环结束为止 - Pointy
@Pointy,我对事件循环的工作原理并不是很了解。你所说的“结束”事件循环是什么意思?我认为事件循环包含一个事件队列,它会一直读取(直到选项卡关闭)。因此,在这个意义上,我不太明白“结束”事件循环到底是什么意思。 - Bharat Khatri
1
可能这个问题本身就没有意义,因为显然 WebKit 没有遵循规范。我会添加一个答案,因为在评论中很难打出一个好的解释 :) - Pointy
1个回答

4
我阅读了相关的W3C规范,以及这篇博客文章末尾Ian Hickson的评论,这表明应该发生的情况是浏览器全局互斥锁控制对每个域的localStorage的访问。每个JavaScript执行的单独“线程”(下面会解释其含义)在检查本地存储时必须尝试获取存储互斥锁(如果它还没有)。一旦获得互斥锁,它不会放弃直到完全完成。
现在,什么是“线程”,线程何时“完成”?唯一有意义的解释(也是与Hixie声称互斥体使事情“完全安全”相一致的解释)是:一个“线程”是由某些事件初始化的浏览器上下文中的JavaScript代码。(请注意,可能的事件之一是刚加载了一个<script>块。)通常情况下,JavaScript在浏览器中的本质是,在<script>块中的代码或任何类型事件的处理程序中的代码运行直到停止;也就是说,运行到<script>主体的结尾,或者运行直到事件处理程序返回。
因此,鉴于这一点,存储互斥体应该强制所有共享域脚本在尝试声明互斥体时阻塞,当它们中的一个已经拥有它时。它们将被阻塞,直到拥有线程完成——直到<script>标记代码用完,或者直到事件处理程序返回。这种行为将实现规范中的这个保证:
因此,在脚本执行期间,Storage对象的length属性和该对象的各种属性的值不能改变,除非以脚本自身可预测的方式改变。
然而,似乎基于WebKit的浏览器(Chrome和Safari,可能还包括Android浏览器,现在也可能包括Opera)不会使用互斥实现,这会让你陷入驱使你提出问题的境地。如果你关心这样的竞争条件(一个完全合理的态度),那么你可以使用博客文章中建议的锁定机制(由一个曾经为Stackoverflow工作的人提出),或者实现一个版本计数系统来检测脏写入。(编辑 - 现在我想起来了,类似RDBMS的版本机制会有问题,因为仍然会有竞争条件来检查版本!)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接