简体   繁体   English

如何在javascript中处理竞争条件?

[英]How to deal with race conditions in javascript?

First off all, I'm a pythonist, not a javascripter - please be kind. 首先,我是一个蟒蛇,而不是一个javascripter - 请善待。

In a popular MVVM Javascript framework from a popular Internet search provider , there is a class called Scope. 流行的Internet搜索提供程序的流行MVVM Javascript 框架中 ,有一个名为Scope的类。

This class has a method called $watch , used to register callbacks (called listeners) that are supposed to be called every time that Scope.$digest is fired. 这个类有一个名为$watch的方法,用于注册每次触发Scope.$digest时应该调用的回调(称为侦听器)。 Calls to Scope.$watch returns a deregistration function for this listener. 调用Scope.$watch返回此侦听器的注销函数。

Listeners are called in the following loop: 在以下循环中调用侦听器:

...
do { // "traverse the scopes" loop
  if ((watchers = current.$$watchers)) {
    // process our watches
    length = watchers.length;
    while (length--) {
      try {
        watch = watchers[length];
        // Most common watches are on primitives, in which case we can short
        // circuit it with === operator, only when === fails do we use .equals
          if ((value = watch.get(current)) !== (last = watch.last)
... 

Since a listener can be registered or destroyed from another listener, there are two race conditions here : 由于侦听器可以从另一个侦听器注册或销毁, 因此这里有两种竞争条件

  1. when a listener is deregistered between length = watchers.length and watch = watchers[length] , watch will be undefined and the call to watch.get(current) will fail (undefined has no method get). 当一个监听器在length = watchers.lengthwatch = watchers[length] watch.get(current) watch = watchers[length]之间注销时, watch将是未定义的,并且对watch.get(current)的调用将失败(undefined没有方法get)。
  2. when a listener is registered by another listener it may be skipped. 当一个监听器被另一个监听器注册时,它可能会被跳过。

I guess a simple check for the existence of watch would fix condition #1: 我猜一个关于watch存在的简单检查会解决条件#1:

if (watch && (value = watch.get(current)) !== (last = watch.last)

I'm not sure how to solve condition #2. 我不确定如何解决条件#2。 Shall not modify an array while iterating over it - my first thought was: why while (length--) ? 迭代时不应修改数组 - 我的第一个想法是:为什么while (length--) Then I read in the source comments: 然后我在源评论中读到:

  • Loop operations are optimized by using while(count--) { ... } 使用while(count--){...}优化循环操作
    • this means that in order to keep the same order of execution as addition we have to add items to the array at the beginning (shift) instead of at the end (push) 这意味着为了保持与添加相同的执行顺序,我们必须在开头(shift)而不是在结尾(push)将数据添加到数组中

In Python I would probably try to solve it using queues. 在Python中,我可能会尝试使用队列来解决它。 Two questions: 两个问题:

  1. What is the idiomatic way of dealing with this problem in javascript when targeting maximum performance and memory conservation? 在针对最大性能和内存保护时,在javascript中处理此问题的惯用方法是什么?
  2. How should I go about unit tests for race conditions (or should I use end-to-end tests instead)? 我应该如何针对竞争条件进行单元测试(或者我应该使用端到端测试)?

[edit] [编辑]

Samuel Neff commented that since JavaScript is not multi-threaded, this is not really a race condition. Samuel Neff评论说,由于JavaScript不是多线程的,因此这不是真正的竞争条件。

A more objective question is: with performance and memory footprint in mind, what can I do to prevent bugs caused by a callback that modifies the very array I'm iterating over when using the while (length--) pattern to loop over an array of callbacks? 一个更客观的问题是:考虑到性能和内存占用,我可以做些什么来防止回调引起的错误修改我正在迭代的数组,当使用while (length--)模式循环数组时回调?

JavaScript is not multi-threaded. JavaScript不是多线程的。 You don't have to worry about another "thread" modifying the objects between sequential lines of code. 您不必担心在连续的代码行之间修改对象的另一个“线程”。

It is possible to have race conditions if you depend on callbacks being called back in a particular order, but you don't have to worry about race conditions like in normal multi-threaded languages. 如果您依赖于以特定顺序回调的回调,则可能存在竞争条件,但您不必担心普通多线程语言中的竞争条件。

The way I have solved problems like this a few times in the past (when iterating through connections in server code, for example) is by adding new listeners (or in my case, connections) to a temporary array / "queue." 我过去几次解决这类问题的方式(例如,在服务器代码中迭代连接时)是通过向临时数组/“队列”添加新的侦听器(或者在我的情况下,连接)。

In my main loop I would go first go through the existing connections, deleting the ones which had been marked as dropped and dispatching other threads to deal with connections which needed servicing. 在我的主循环中,我将首先浏览现有连接,删除已标记为已删除的连接并调度其他线程以处理需要服务的连接。 Other threads could register connections to the queue array or mark connections as dropped from the main array but would not actually delete them. 其他线程可以注册到队列数组的连接或标记从主数组中删除的连接,但实际上不会删除它们。 This took care of your first race condition. 这照顾了你的第一场比赛条件。

The second race condition was solved by having my main loop atomically link the queue array to the end of the main array thus starting an empty queue array and extending the main array by the previous contents of the queue array. 第二个竞争条件是通过让我的主循环将队列数组原子地链接到主数组的末尾来解决的,从而启动空队列数组并通过队列数组的先前内容扩展主数组。 The main loop then finished going through the newly extended contents of the main array. 然后主循环完成了主数组的新扩展内容。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM