从另一个线程修改 ConcurrentBag 对象

Modifying ConcurrentBag objects from another thread

我有一个线程 #1,它通过 ConcurrentBag 列表与 foreach 连续循环并修改项目,但有时另一个线程 #2 需要修改列表中的项目。如果我从线程 #2 中取出所有对象(而另一个对象正在使用 foreach 循环)并修改对象然后将所有项目添加回线程 #2 是否安全?如果不是,那么有解决方法吗?

ConcurrentBag<MyObject> list;

线程 1

while (1) // continuous loop
{
  foreach(MyObject obj in list)
  {
     obj.RunMethod();
     Thread.Sleep(500);
  }
  Thread.Sleep(1000);
}

线程 2

(在某些时候在线程 #2 上调用回调并需要修改对象)

List<MyObject> temp;
while (list.TryTake(out obj)
{
   MyObject obj2=obj.Clone(); // make a copy of the object
   if (obj2.Id==4) obj2.variable=10;
   temp.Add(obj2);
}
// Add objects back to concurrentbag
foreach(MyObject obj in temp) list.Add(obj);

不,这不安全,因为执行 foreach 的线程可能已经检索到一个对象的引用并正在使用该对象,而另一个线程将其从包中移除、修改并放入返回。

这可能会导致对象的状态在 foreach 线程使用它时发生变化。您可以通过创建要变异对象的副本,然后更改副本并将副本放回包中来避免这种情况。

你考虑过让你的对象不可变吗?这样做真的会简化事情,实际上使两个线程不可能同时改变它。

注意:复制对对象的引用不会复制对象的内容,因此当您这样做时 list.TakeOut(out obj) 您收到的是对现有对象的引用,而不是创建它的新副本。

[编辑] 所以您编辑了您的问题以使用 obj.Clone()。如果这对您的对象进行了适当的深度克隆,那么代码 应该 没问题,但前提是您小心地将对象复制到任何更改的地方。如果你使 class 不可变,那么你保证了这种行为,所以这是最好的解决方案,如果你可以使用它。