C#Task.run(),将参数传递给

时间:2017-10-18 07:35:36

标签: c# asynchronous concurrency task

考虑以下代码

attempt = 0;
for (int counter = 0; counter < 8; counter++)
{
    if (attempt < totalitems)
    {
        Tasklist<output>.Add(Task.Run(() =>
        {
            return someasynctask(inputList[attempt]);
        }));
    }
    else
    {
        break;
    }
    attempt++;
}
await Task.WhenAll(Tasklist).ConfigureAwait(false);

我希望有8个并发任务,每个任务同时处理不同的输入,最后检查结果,所有这些任务完成后。 因为我没有等待Task.Run()的完成尝试在任务开始之前增加,并且当任务开始时,输入列表中可能有一些项目未被处理或处理两次或更多次(因为尝试价值的不确定性。

怎么做?

2 个答案:

答案 0 :(得分:2)

问题在于使用“lambda”:在执行期间达到Task.Run(() => return someasynctask(inputList[attempt]));时,捕获变量attempt,而不是其值(即它是“闭包”)。因此,当执行lambda时,将使用该特定时刻的变量值。

只需在lambda之前添加变量的临时副本,然后使用它。 E.g。

if (attempt < totalitems)
{
    int localAttempt = attempt;
    Tasklist<output>.Add(Task.Run(() =>
    {
        return someasynctask(inputList[localAttempt]);
    }));
}

答案 1 :(得分:1)

感谢@gobes的回答:

试试这个:

attempt = 0;
for (int counter = 0; counter < 8; counter++)
{
    if (attempt < totalitems)
    {
        Tasklist<output>.Add(Task.Run(() =>
        {
            int tmpAttempt = attempt;
            return someasynctask(inputList[tmpAttempt]);
        }));
    }
    else
    {
        break;
    }
    attempt++;
}
await Task.WhenAll(Tasklist).ConfigureAwait(false);

实际上,编译器正在做的是将lambda提取到一个方法中,该方法位于一个自动生成的类中,该类引用了try变量。这一点非常重要:生成的代码只引用另一个类的变量;它没有复制它。因此,方法可以看到尝试的每一个变化。

执行期间发生的事情大致如下:

输入try = 0的循环 将lambda-like-method的调用添加到任务列表中 增加尝试 重复 在循环之后,你有一些等待的方法调用(没有双关语),但是每个方法调用都引用了相同的变量,因此共享它的值 - 最后一个受其影响。

有关更多详细信息,我真的建议深入阅读C#,或者同类书籍 - 在网络上有很多关于C#闭包的资源:)