multithreading - 从Dart中的多个异步流修改对象

标签 multithreading dart concurrency stream

想象我们有一个这样的物体

class Foo {
  List<int> data = [];

  void addAndCheck(int n){
    for(int number in data){
        // check something
    }
    data.add(n);
  }
}

想象我们产生了很多这样的订阅
Foo foo = Foo();

for(int i = 0; i++; i<10){
  subscriptions.add(api.someRandomStream().listen((response){
    foo.addAndCheck(response.value);
  }));
}

就目前而言,如果运行此代码可能会起作用,但是只要流大约在同一时间开始发出流,我们就会收到异常:Concurrent modification during iteration
原因是for循环,但是如何解决此问题?在Java之类的语言中,有ConcurrentHashMap,Collections.synchronizedList(...)等东西。

最佳答案

如果在迭代过程中遇到并发修改错误,则说明您正在循环内执行异步操作。也就是说,您的函数可能是async,并且循环内至少有一个await。这将使您在等待时触发另一个事件,然后修改列表。

有几种避免异常的方法,所有方法都需要权衡取舍:

  • 不要在循环中执行任何异步操作,并确保您在那里执行的任何操作都不会再次调用addAndCheck。这样就不会有问题,因为循环将在其他人有机会修改列表之前完成。显然,这仅在不需要执行异步操作时才有效。
  • 复制列表。如果您执行for(int number in [...data]) { ... }(或以前编写的in data.toList()),则您要迭代的列表与被修改的列表是不同的列表。这也意味着您可能在到达add调用时尚未检查列表中实际存在的所有元素。
  • 不要使用迭代器。如果执行for (int i = 0; i < data.length; i++) { var number = data[i]; ... },则不会从迭代器中收到并发修改错误。如果将元素添加到列表的末尾,那么您最终将到达它们,一切都很好。如果将元素从列表中删除或添加到末尾以外的任何位置,则您可能会跳过元素或两次看到其中的一些元素,这可能对您不利。
  • 使用互斥锁。如果要确保在添加任何其他元素之前对现有元素进行所有测试,则需要防止在添加时发生任何事情。假设某种Mutex类,这将允许您编写如下代码:
    class Foo {
      List<int> data = [];
      final _mutex = Mutex();  
    
      void addAndCheck(int n) async {
        await _mutex.acquire();
        for(int number in data){
          // check something
        }
        data.add(n); 
        _mutex.release();
      }
    }
    

    (我通过搜索找到了 package:mutex ,但我对此没有经验)。
    但是,这可能会使您的代码变慢,使每个操作都等待上一个操作完全完成。

  • 最后,只有您可以说出哪种折衷方案最适合您的代码行为。

    关于multithreading - 从Dart中的多个异步流修改对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59136302/

    相关文章:

    flutter - 如何使用Flutter同Page导航与Getx路由或Material路由?

    flutter - 如何知道返回值是否是 future

    java - 在 OpenCV 中从回调方法获取图像而不阻塞

    java - 在多线程进程中运行存储过程

    dart - 嵌套聚合物自定义组件数据绑定(bind)

    multithreading - cpu松弛是什么意思?

    multithreading - 生产者/消费者模型是否等同于 Actor ?

    c - Pthreads 在执行时更新全局 2D 数组段错误

    c++ - 锁定互斥量与访问磁盘

    python - 为什么 Python 的 GIL 强制执行严格的处理顺序?