考虑以下代码
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()的完成尝试在任务开始之前增加,并且当任务开始时,输入列表中可能有一些项目未被处理或处理两次或更多次(因为尝试价值的不确定性。
怎么做?
答案 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#闭包的资源:)