我正在尝试运行连接到远程站点(通过网络)的多个功能并返回通用列表。但我想同时运行它们。
例如:
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;
}
正如我所看到的,“结果”的多次插入可能会同时发生......这可能会导致我的应用程序崩溃。
我该如何避免这种情况?
答案 0 :(得分:130)
您可以使用concurrent collection。
System.Collections.Concurrent
命名空间提供了几个线程安全的集合类,只要多个线程同时访问集合,就应该使用它们代替System.Collections
和System.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 AsParallel
和SelectMany
简明扼要地表达:
public static List<SearchResult> Search(string title)
{
return Providers.AsParallel()
.SelectMany(p => p.SearchTitle(title))
.ToList();
}