想象我们有一个这样的物体
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/