Parallel.ForEach添加到列表

时间:2011-11-03 20:55:45

标签: c# list locking parallel-processing

我正在尝试运行连接到远程站点(通过网络)的多个功能并返回通用列表。但我想同时运行它们。

例如:

public static List<SearchResult> Search(string title)
{
    //Initialize a new temp list to hold all search results
    List<SearchResult> results = new List<SearchResult>();

    //Loop all providers simultaneously
    Parallel.ForEach(Providers, currentProvider =>
    {
        List<SearchResult> tmpResults = currentProvider.SearchTitle((title));

        //Add results from current provider
        results.AddRange(tmpResults);
    });

    //Return all combined results
    return results;
}

正如我所看到的,“结果”的多次插入可能会同时发生......这可能会导致我的应用程序崩溃。

我该如何避免这种情况?

5 个答案:

答案 0 :(得分:130)

您可以使用concurrent collection

  

System.Collections.Concurrent命名空间提供了几个线程安全的集合类,只要多个线程同时访问集合,就应该使用它们代替System.CollectionsSystem.Collections.Generic命名空间中的相应类型。

您可以使用ConcurrentBag,因为您无法保证将添加项目的顺序。

  

表示线程安全,无序的对象集合。

答案 1 :(得分:45)

//In the class scope:
Object lockMe = new Object();    

//In the function
lock (lockMe)
{    
     results.AddRange(tmpResults);
}

基本上锁定意味着只有一个线程可以同时访问该关键部分。

答案 2 :(得分:22)

Concurrent Collections是.Net 4的新功能;它们旨在使用新的并行功能。

请参阅Concurrent Collections in the .NET Framework 4

  

在.NET 4之前,如果多个线程可能正在访问单个共享集合,则必须提供自己的同步机制。你必须锁定集合......

     

... System.Collections.Concurrent中的[new]类和接口[在.NET 4中添加]为涉及跨线程共享数据的多线程编程问题提供了一致的实现。

答案 3 :(得分:21)

对于喜欢代码的人:

public static ConcurrentBag<SearchResult> Search(string title)
{
    var results = new ConcurrentBag<SearchResult>();
    Parallel.ForEach(Providers, currentProvider =>
    {
        results.Add(currentProvider.SearchTitle((title)));
    });

    return results;
}

答案 4 :(得分:12)

这可以使用PLINQ AsParallelSelectMany简明扼要地表达:

public static List<SearchResult> Search(string title)
{
    return Providers.AsParallel()
                    .SelectMany(p => p.SearchTitle(title))
                    .ToList();
}