Parallel.ForEach与添加到列表


80

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

例如:

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;
}

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

如何避免这种情况?


您正在使用哪个.NET版本?
SLL

4
至少必须是.Net 4;在此引入了并行。
2011年

Answers:


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

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

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


1
但是,如果将这些结果添加到另一个提供商的结果中尝试添加到该结果,将会发生什么?他们会失败还是等待直到可能?
shaharmor 2011年

4
当有锁时,线程将等待直到获得锁为止。
哈德良

因此,基本上它像这样说:等待!results.isLocked,然后在其自由锁定时写入?
shaharmor 2011年

8
要点: this不是锁对象的最安全选择。最好使用特殊的私有对象:lock(resultsLock)
Henk Holterman

2
locks可能会减慢整体执行时间。.并发收集似乎最好避免这种情况
Piotr Kula

155

您可以使用并发集合

System.Collections.Concurrent命名空间提供了应在的地方对应的类型在使用几个线程安全的集合类System.CollectionsSystem.Collections.Generic时多线程并发访问的集合名称空间。

例如,您可以使用,ConcurrentBag因为您不能保证将添加哪个订单。

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


是的,这是实际答案。通过并发集合,您将获得更好的性能(通常)。
lkg

32

对于那些喜欢代码的人:

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

    return results;
}

必须使用循环: foreach (var item in currentProvider.SearchTitle((title))) results.Add(item);
Anthony McGrath

25

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

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

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

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


13

这可以使用PLINQAsParallel和简洁地表达SelectMany

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

linq select很多很棒,可悲的是linq比普通的foreach慢。:(
Kugan Kumar

6
不要进行微优化。OP表示已SearchTitle连接到远程站点。它的延迟将比LINQ和foreach之间的差异慢几个数量级。
道格拉斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.