C# 正在Parallel.For内修改的方法局部变量。这个线程有多安全?
我有以下代码片段:C# 正在Parallel.For内修改的方法局部变量。这个线程有多安全?,c#,multithreading,thread-safety,task-parallel-library,parallel.foreach,C#,Multithreading,Thread Safety,Task Parallel Library,Parallel.foreach,我有以下代码片段: int totalData = result.Data.Count; int count = 0; Parallel.ForEach(result.Data, data => { try { EventRange importedEntity = ImportEntity(auxResult.EntityName, data); count++; EntityImported(importedEntity,
int totalData = result.Data.Count;
int count = 0;
Parallel.ForEach(result.Data, data =>
{
try
{
EventRange importedEntity = ImportEntity(auxResult.EntityName, data);
count++;
EntityImported(importedEntity, count, totalData);
}
catch (Exception e)
{
exceptions.Enqueue(e);
}
});
EntityImported是一个事件,它应该说明已经处理了多少实体,以及我应该处理多少实体。我担心的是lambda中递增count的线程安全性,以及您建议采取哪些步骤来确保总是使用正确的count变量值触发事件。目前,它根本不是线程安全的 您可以使用
Interlocked.Increment(ref count)
来代替,但通常最好为每个线程设置一个“local”值,并在最后求和。这样,除了分配要处理的项之外,您不需要任何类型的跨线程数据流
这里有一个为这个目的而设计的重载—请看文档底部附近的示例,因为它所做的事情与您的代码非常相似。(它维护一个局部计数,然后在最后对计数求和。)不幸的是,这对您的特定情况没有帮助,因为您需要在每次迭代中引发事件的方式——但通常这是一种更好的方法
在这种情况下,您应该在事件引发代码中使用Interlocked.Increment
的结果:
Parallel.ForEach(result.Data, data =>
{
try
{
EventRange importedEntity = ImportEntity(auxResult.EntityName, data);
int newCount = Interlocked.Increment(ref count);
EntityImported(importedEntity, newCount, totalData);
}
catch (Exception e)
{
exceptions.Enqueue(e);
}
});
这样,每次计数将只引发一个事件(一个为0,一个为1,一个为2等)。谢谢Jon,尽管我不确定我是否理解该重载如何帮助我。如果我理解正确,它允许我将某个值传递给lambda,以正常方式求和,然后使用它执行最终操作,以最小化必要的联锁操作量。如果我需要用处理过的实体的总量进行操作,它将为我服务。然而,我的问题是,我需要在处理每个实体时,用当前处理的实体数量引发一个事件。我是不是误会了什么,或者超载对我没有好处?@Uri:啊,我错过了。在这种情况下,请退回到使用Interlocked.Increment。@Uri:在Interlocked.Increment和调用EntityImported之间,count可能已经更改。优化器可能会针对该值优化返回内存的输出,但可能不会。它可能没有那么快,但您可能希望在lambda中创建一个局部变量,在递增count之前锁定,然后复制count local。eg=>importedEntity=blah();内部计数;lock(lock_object){interlocked.inc(count);internalcount=count;}EntityImported(count,importedEntity)@本吉:比赛是一个很好的观点,但没有必要使用锁定。只需使用
Interlocked.Increment的返回值即可。有关示例代码,请参阅我编辑过的答案。@JonSkeet:我刚刚学到了一些新的,Interlocked.Increment返回值。谢谢