0

我正在尝试使用多线程来更快地处理结果列表。我尝试为每个使用并行,但是当运行过程方法时,我没有收到正确的结果。

private IEnumerable<BulkProcessorResult> GetProccessResults(List<Foo> Foos)
{
    var listOfFooLists = CreateListOfFooLists(Foos);

    var bulkProcessorResults = new List<BulkProcessorResult>();
    Parallel.ForEach(listOfFooLists, FooList =>
    {
        foreach (var Foo in FooList)
        {
            var processClaimResult = _processor.Process(Foo);
            var bulkProcessorResult = new BulkProcessorResult()
            {
                ClaimStatusId = (int) processClaimResult.ClaimStatusEnum,
                Property1 = Foo.Property1
            };
            bulkProcessorResults.Add(bulkProcessorResult);
        }
    }); 

    return bulkProcessorResults;
}

如果我使用普通的 forEach,我会得到正确的输出。如果我使用上面的代码,当应该有三个状态为 1 和一个状态为 3 时,我会得到所有 2 的状态。

我对线程真的很陌生,所以任何帮助都会很棒。

4

2 回答 2

3

最明显的问题是您正在使用多个线程(好吧,这在某种程度上被调用隐藏了Parallel.ForEach,但是您应该知道它通过使用多个线程/任务来实现并行性)但是您使用的是 a List<T>,这不是一个线程安全的集合类:

AList<T>可以同时支持多个读取器,只要不修改集合即可。通过集合进行枚举本质上不是线程安全的过程。在枚举与一个或多个写访问竞争的极少数情况下,确保线程安全的唯一方法是在整个枚举期间锁定集合。要允许集合被多个线程访问进行读写,必须实现自己的同步

不过,我不会实现自己的同步,也不会在代码中改变太多其他内容,而是改用ConcurrentQueue<T>

private IEnumerable<BulkProcessorResult> GetProccessResults(List<Foo> Foos)
{
    var listOfFooLists = CreateListOfFooLists(Foos);

    var bulkProcessorResults = new ConcurrentQueue<BulkProcessorResult>();
    Parallel.ForEach(listOfFooLists, FooList =>
    {
        foreach (var Foo in FooList)
        {
            var processClaimResult = _processor.Process(Foo);
            var bulkProcessorResult = new BulkProcessorResult()
            {
                ClaimStatusId = (int) processClaimResult.ClaimStatusEnum,
                Property1 = Foo.Property1
            };
            bulkProcessorResults.Enqueue(bulkProcessorResult);
        }
    }); 

    return bulkProcessorResults;
}
于 2013-06-03T13:54:16.773 回答
1

将整个事情视为 Parallel Linq 查询怎么样?

private IEnumerable<BulkProcessorResult> GetProccessResults(List<Foo> Foos)
{
  var listOfFooLists = CreateListOfFooLists(Foos);
  return listOfFooLists.AsParallel()
                       .SelectMany(FooList => FooList)
                       .Select(Foo =>
                            new BulProcessorResult {
                               ClaimStatusId = (int)_processor.Process(Foo),
                               Property1 = Foo.Property1
                            }).ToList();
}
于 2013-06-03T13:34:20.177 回答