在Parallel.For中修改方法局部变量。这个线程安全吗?

时间:2014-06-04 13:41:30

标签: 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, count, totalData);
    }
    catch (Exception e)
    {
        exceptions.Enqueue(e);
    }
});

EntityImported是一个事件,应该说明已经处理了多少实体,以及我应该处理多少实体。我担心的是在lambda内增加count的线程安全性,以及你建议采用哪些步骤来确保事件始终使用count变量的正确值来触发。

1 个答案:

答案 0 :(得分:9)

目前它根本不是线程安全的。

您可以改为使用Interlocked.Increment(ref count),但 更好地拥有" local"每个线程的值,并在最后总结它们。这样,除了分配要处理的项目之外,您不需要任何类型的跨线程数据流。

Parallel.ForEach的重载是为了这个目的而设计的 - 请查看文档底部附近的示例,因为它正在做非常类似的事情你的代码。 (它维护一个本地计数,然后在最后对计数求和。)不幸的是,由于你需要在每次迭代时引发事件的方式,这对你的特定情况没有帮助 - 但通常那是一种更好的方法。

在这种情况下,您应该在事件提升代码中使用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等)。