Parallel.ForEach添加到列表

sha*_*mor 69 c# parallel-processing locking list

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

例如:

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;
}
Run Code Online (Sandbox Code Playgroud)

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

我怎么能避免这个?

Mar*_*ers 141

您可以使用并发集合.

System.Collections.Concurrent命名空间提供了应该发生在相应类型的使用几个线程安全的集合类System.CollectionsSystem.Collections.Generic命名空间,只要多线程并发访问的集合.

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

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

  • [`ConcurrentQueue&lt;T&gt;`](https://learn.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentqueue-1) 可能比 `ConcurrentBag&lt;T&gt;` 更好,因为前者保留了插入顺序。`ConcurrentBag&lt;T&gt;` 是一个[极其专业](/sf/ask/1078009341/#64823123)集合旨在用于混合生产者消费者场景,并且并行添加项目(不从集合中获取项目)不是这样的场景。 (2认同)

Hae*_*ian 51

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

//In the function
lock (lockMe)
{    
     results.AddRange(tmpResults);
}
Run Code Online (Sandbox Code Playgroud)

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

  • 一个小问题:`this`不是锁定对象最安全的选择.最好使用一个特殊的私有对象:`lock(resultsLock)`. (7认同)
  • 当有锁时,线程将一直等到它可以锁定. (3认同)
  • `locks`虽然可以减慢整体执行时间..并发集合似乎更好地避免了这一点 (2认同)

aro*_*eer 23

并发集合是.Net 4的新功能; 它们旨在使用新的并行功能.

请参阅.NET Framework 4中的并发集合:

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

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


Mat*_*ius 23

对于喜欢代码的人:

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

    return results;
}
Run Code Online (Sandbox Code Playgroud)


Dou*_*las 12

这可以用简洁PLINQ的表达AsParallelSelectMany:

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

  • 不要微观优化.OP暗示`SearchTitle`连接到远程站点.它的延迟将比LINQ和foreach之间的差异慢几个数量级. (3认同)