作为参考,我使用.net 4.6.2。
我有一个私有方法,它可以进行计算并将计算结果添加到ConcurrentBag中。
private void doSomething(List<myClass> list, ConcurrentBag<resClass> cb)
{
// do calcs over items in list
cb.Add(res);
}
字符串
在外面,我这样称呼它:
ConcurrentBag<resClass> cb = new ConcurrentBag<resClass>();
List<Task> bagAddTasks = new List<Task>();
foreach(var item in myList)
{
List<myClass> myClassList = new List<myClass>() { item };
bagAddTasks.Add(Task.Run(() => doSomething(myClassList, cb));
}
// Consume the items in the bag
Task.WaitAll(bagAddTasks.ToArray());
List<Task> bagConsumeTasks = new List<Task>();
int itemsInBag = 0;
while (!cb.IsEmpty)
{
bagConsumeTasks.Add(Task.Run(() =>
{
resClass x;
if (cb.TryTake(out x))
{
resClassList.Add(x);
Interlocked.Increment(ref itemsInBag);
}
}));
}
Task.WaitAll(bagConsumeTasks.ToArray());
型
有时候,传递给doSomething的参数“list”中的项会被重置,就像创建了一个新示例一样,因此它的所有属性都为null。
有人能帮我弄清楚为什么会发生这种情况,以及我如何解决它?
1条答案
按热度按时间u7up0aaq1#
您至少有一个竞争条件,不能保证
while (!cb.IsEmpty)
会被输入。doSomething
开始添加项目需要一些时间,即使它已经启动,也不能保证您会为cb
中的每个项目运行bagConsumeTasks.Add
一次。可能会更多,可能会更少,但它可能不正确。这可能会导致一些其他组件开始修改线程之间无意中共享的对象的问题。
我不知道bagConsumeTasks应该做什么,如果你想确保你正在等待所有任务完成,只需运行
Task.WaitAll(bagAddTasks)
。每当你使用并发集合时,你需要确保你在多个调用之间没有依赖关系,因为集合可能会改变。这就是像TryTake
这样的方法的原因,它既检查是否有项目,又返回它们,在单个线程安全调用中。我强烈建议你使用更高级别的原语,比如BlockingCollection或DataFlow,这应该会让像这样的简单错误变得更难。