在foreach中启动任务循环使用最后一项的值

我正在尝试玩新的任务,但发生了一些我不明白的事情。

首先,代码非常简单。 我传递一些路径到一些图像文件,并尝试添加一个任务来处理它们中的每一个:

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

我发现如果我让这个运行,比如单元测试中的3个路径的列表,所有三个任务都使用提供的列表中的最后一个路径。 如果我逐步完成(并减慢循环的处理),则将使用循环中的每条路径。

有人可以解释发生了什么,为什么? 可能的解决方法?


你正在关闭循环变量。 不要这样做。 改为复制一份:

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 - 不是创建任务时的值,而是变量本身。 每次你通过循环时,这个变量都会改变值 - 所以在你的委托被调用的时候它可以很容易地改变。

通过获取变量的副本,每当您经历循环时都会引入一个新变量 - 当您捕获该变量时,它将不会在循环的下一次迭代中更改。

Eric Lippert有一对博客文章,详细介绍了这一点:第1部分; 第2部分。

不要感觉不好 - 这几乎抓住了每个人都:(


您传递给StartNew的lambda引用了path变量,该变量在每次迭代中都会发生变化(即您的lambda正在使用path的引用,而不仅仅是它的值)。 您可以创建它的本地副本,以便您不指向会更改的版本:

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);
}
链接地址: http://www.djcxy.com/p/52783.html

上一篇: Starting Tasks In foreach Loop Uses Value of Last Item

下一篇: Action delegate uses the last values of variables declared outside foreach loop