我有一个线程#1,它通过ConcurrentBag列表与foreach一起循环并修改项目,但是在某个时候另一个线程#2需要修改列表中的项目。如果我从线程#2取出所有对象(而另一个用foreach循环)并修改对象然后在线程#2中添加所有项目,这是否安全?如果不是那么有解决方法吗?
ConcurrentBag<MyObject> list;
while (1) // continuous loop
{
foreach(MyObject obj in list)
{
obj.RunMethod();
Thread.Sleep(500);
}
Thread.Sleep(1000);
}
(在某些时候,在线程#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);
答案 0 :(得分:4)
不,这是不安全的,因为执行foreach
的线程可能已检索到对象的引用并正在使用该对象,而另一个线程将其从包中移除,修改它并将其放回。
这可能导致对象的状态在list.TakeOut(out obj)
线程正在使用时发生变化。您可以通过创建要变异的对象的副本来避免这种情况,然后更改副本并将副本放回到包中。
您是否考虑过使您的对象不可变?这样做会真正简化事情,实际上两个线程不可能同时改变它。
注意:复制对象的引用不会复制对象的内容,因此当您执行obj.Clone()
时,您将收到对现有对象的引用,而不是创建它的新副本。
[编辑]因此,您修改了问题以使用ContentType
。如果这对您的对象进行了正确的深度克隆,那么代码应该就可以了,但这只是在您需要将对象复制到更改的地方时。如果你使类不可变,那么你保证这种行为,这是你可以使用它的最佳解决方案。