在foreach中启动任务循环使用最后一项的值
我正在尝试玩新的任务,但有些事情我不明白。
首先,代码非常简单。 我传递一些path到一些图像文件,并尝试添加一个任务来处理它们中的每一个:
public Boolean AddPictures(IList<string> paths) { Boolean result = (paths.Count > 0); List<Task> tasks = new List<Task>(paths.Count); foreach (string path in paths) { var task = Task.Factory.StartNew(() => { Boolean taskResult = ProcessPicture(path); return taskResult; }); task.ContinueWith(t => result &= t.Result); tasks.Add(task); } Task.WaitAll(tasks.ToArray()); return result; }
我发现,如果我让这个运行,比如一个unit testing中的3个path列表,所有这三个任务都使用提供的列表中的最后一个path。 如果我一步一步(并减慢循环的处理),则使用循环中的每条path。
有人可以解释发生了什么,为什么? 可能的解决方法?
你正在closures循环variables。 不要这样做。 取一个副本:
foreach (string path in paths) { string pathCopy = path; var task = Task.Factory.StartNew(() => { Boolean taskResult = ProcessPicture(pathCopy); return taskResult; }); task.ContinueWith(t => result &= t.Result); tasks.Add(task); }
您当前的代码正在捕获path
– 而不是创build任务时的值 ,而是variables本身。 每次你通过循环时,这个variables都会改变值 – 所以在你的委托被调用的时候,它可以很容易地改变。
通过获取variables的副本,每次循环都会引入一个新variables – 捕获该variables时,在循环的下一次迭代中不会更改。
Eric Lippert有一对博客文章详细介绍了这一点: 第1部分 ; 第2部分 。
不要觉得不舒服 – 这几乎每个人都赶出:(
传递给StartNew
的lambda引用了path
variables,每次迭代都会改变(即,lambda正在使用path
的引用 ,而不仅仅是它的值)。 您可以创build它的本地副本,以便您不指向将会更改的版本:
foreach (string path in paths) { var lambdaPath = path; var task = Task.Factory.StartNew(() => { Boolean taskResult = ProcessPicture(lambdaPath); return taskResult; }); task.ContinueWith(t => result &= t.Result); tasks.Add(task); }
- 通过等待任务或访问它的Exception属性,没有观察到任务的exception。 结果,没有被察觉的例外是
- 如果使用带有大对象的枚举,Parallel.ForEach可能会导致“内存不足”exception
- 如何在wpf背景中执行任务,同时能够提供报告并允许取消?
- 捕获asynchronous方法引发的exception
- 在TPL Task对象上调用Dispose()是否可以接受?
- 同步等待asynchronous操作,为什么Wait()在这里冻结程序
- ConfigureAwait将继续推送到池线程
- C#中Task.FromResult <TResult>的用法是什么
- 如何创build一个运行STA线程的任务(TPL)?