我正在并行调用一个缓慢的Web服务。事情很棒,直到我意识到我需要从服务中获取一些信息。但我不知道在哪里可以获得价值。我无法写入数据库,HttpContext.Current似乎在使用Parallel.ForEach调用的方法中为null
下面是一个示例程序(在您看来,请想象一个慢速Web服务而不是字符串连接)
using System;
using System.Threading.Tasks;
class Program
{
static void Main(string[] args)
{
WordMaker m = new WordMaker();
m.MakeIt();
}
public class WordMaker
{
public void MakeIt()
{
string[] words = { "ack", "ook" };
ParallelLoopResult result = Parallel.ForEach(words, word => AddB(word));
Console.WriteLine("Where did my results go?");
Console.ReadKey();
}
public string AddB(string word)
{
return "b" + word;
}
}
}
答案 0 :(得分:60)
你已经放弃了它。
ParallelLoopResult result = Parallel.ForEach(words, word => AddB(word));
你可能想要类似的东西,
ParallelLoopResult result = Parallel.ForEach(words, word =>
{
string result = AddB(word);
// do something with result
});
如果您想在此结束时使用某种类型的集合,请考虑使用System.Collections.Concurrent
下的其中一个集合,例如ConcurrentBag
ConcurrentBag<string> resultCollection = new ConcurrentBag<string>();
ParallelLoopResult result = Parallel.ForEach(words, word =>
{
resultCollection.Add(AddB(word));
});
// Do something with the result
答案 1 :(得分:22)
您可以考虑使用AsParallel
的{{1}}扩展方法,它将为您处理并发并收集结果。
IEnumerable
同步(例如,使用锁的锁或并发集合)通常是并发算法的瓶颈。最好的方法是尽可能避免同步。我猜测
words.AsParallel().Select(AddB).ToArray()
使用更聪明的东西,例如将在单个线程上生成的所有项目放入本地非并发集合中,然后在最后组合这些项目。
答案 2 :(得分:11)
请勿使用ConcurrentBag
来收集结果,因为它非常慢。
改为使用本地锁。
var resultCollection = new List<string>();
object localLockObject = new object();
Parallel.ForEach<string, List<string>>(
words,
() => { return new List<string>(); },
(word, state, localList) =>
{
localList.Add(AddB(word));
return localList;
},
(finalResult) => { lock (localLockObject) resultCollection.AddRange(finalResult); }
);
// Do something with resultCollection here
答案 3 :(得分:3)
这样的事情怎么样:
public class WordContainer
{
public WordContainer(string word)
{
Word = word;
}
public string Word { get; private set; }
public string Result { get; set; }
}
public class WordMaker
{
public void MakeIt()
{
string[] words = { "ack", "ook" };
List<WordContainer> containers = words.Select(w => new WordContainer(w)).ToList();
Parallel.ForEach(containers, AddB);
//containers.ForEach(c => Console.WriteLine(c.Result));
foreach (var container in containers)
{
Console.WriteLine(container.Result);
}
Console.ReadKey();
}
public void AddB(WordContainer container)
{
container.Result = "b" + container.Word;
}
}
我相信锁定或并发对象是没有必要的,除非你需要结果相互交互(就像你计算一个总和或组合所有单词)。在这种情况下,ForEach巧妙地打破了你的原始列表并将每个线程交给它自己的对象,它可以操纵它想要的所有内容,而不必担心干扰其他线程。
答案 4 :(得分:1)
这似乎安全,快速,简单:
public string[] MakeIt() {
string[] words = { "ack", "ook" };
string[] results = new string[words.Length];
ParallelLoopResult result =
Parallel.For(0, words.Length, i => results[i] = AddB(words[i]));
return results;
}