从另一个线程修改 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 不可变,那么你保证了这种行为,所以这是最好的解决方案,如果你可以使用它。
我有一个线程 #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 不可变,那么你保证了这种行为,所以这是最好的解决方案,如果你可以使用它。