以下代码创建正确数量的文件,但每个文件都包含第一个列表的内容。有人能发现我做错了吗?
private IList<List<string>> GetLists()
{
// Code omitted for brevity...
}
private void DoSomethingInParallel()
{
var lists = GetLists();
var tasks = new List<Task>();
var factory = new TaskFactory();
foreach (var list in lists)
{
tasks.Add(factory.StartNew(() =>
{
WriteListToLogFile(list);
}));
}
Task.WaitAll(tasks.ToArray());
}
答案 0 :(得分:4)
原因在于C#评估匿名方法的方式,它们不是真正的闭包。它实际上与TPL无关。以下代码打印出所有的d。这不是你期望的那样
List<Task> tasks = new List<Task>();
List<string> lists = new List<string>();
lists.AddRange(new string[] { "a", "b", "c", "d" });
foreach (var list in lists)
{
tasks.Add(Task.Factory.StartNew(() =>
{
Console.WriteLine(list);
}));
}
原因是因为创建时匿名方法列表的值不是在方法体中评估的值。使用方法执行时列表的值。您可以通过执行以下操作强制修复此问题:
List<Task> tasks = new List<Task>();
List<string> lists = new List<string>();
lists.AddRange(new string[] { "a", "b", "c", "d" });
foreach (var list in lists)
{
var localList = list;
tasks.Add(Task.Factory.StartNew(() =>
{
Console.WriteLine(localList);
}));
}
您不必明确地将列表值传递给匿名方法。
这篇博文更详细地介绍了这一点:
http://blogs.msdn.com/b/abhinaba/archive/2005/10/18/482180.aspx
答案 1 :(得分:1)
抱歉不回复此问题。我找到了一个解决方案 - 虽然我不明白它为什么会起作用......
最初,我有这个......
foreach (var list in lists)
{
tasks.Add(factory.StartNew(() =>
{
WriteListToLogFile(list);
}));
}
将顺序foreach更改为并行foreach可以解决问题......
Parallel.ForEach<string>(lists, list =>
tasks.Add(factory.StartNew(() =>
{
WriteListToLogFile(list);
}));
);
答案 2 :(得分:0)
我不确定为什么你有一个“任务”列表,你只使用其中一个。
编辑: factory.StartNew创建并启动System.Threading.Tasks.Task !!
大声思考:
所以在其列表中的每个List<String>
都有一个单独的任务,它调用WriteListToLogFile?
我认为你需要使用
ThreadPool.QueueUserWorkItem
在task.Add 之后的代码中
查看此示例(请参阅接受的答案帖子)link
答案 3 :(得分:0)
自己也遇到同样的问题。我仍然不确定它为什么会发生,但我能够通过传递一个状态对象让它正常工作
foreach (var list in lists)
{
tasks.Add(factory.StartNew((o) =>
{
var l = o as List<string>;
WriteListToLogFile(l);
}, list));
}