使用外部变量的lambda实际上捕获变量,而不是存储在其中的值。这意味着随着循环的进行,您从捕获的变量中读取的值也会改变。
你可以通过在循环中使用一个临时变量来解决这个问题。您的代码将是更清洁了很多但如果你使用async/await
代替ContinueWith和lambda表达式,如:
for (int i=0;i<N;i++)
{
//...
var result=await thatOtherAsyncMethod(...);
ProcessResult(i, result));
}
在一般情况下,你可以通过复制循环变量进入循环的范围内定义的变量避免捕获的问题。
这解决了问题,因为临时变量只存在于循环体内。拉姆达是也循环体内创建,捕捉局部的,不变的变量:
for (int i=0;i<N;i++)
{
var temp=i;
var myLambda = new Action(()=>MyMethod(temp));
//This runs with the local copy, not i
myLambda();
}
的虽然更好的方法,是避免捕获和传递回路值作为状态参数ContinueWith
,例如:
for (int i = 0; i < N; ++i)
{
//...
var task = anotherTask.ContinueWith(
(t,state) => ProcessResult((int)state, t.Result),
i);
//...
}
为什么使用'ContinueWith'而不是等待?至于'i',你的lambda捕获*变量*而不是变量的值。当实际读取它时,读取'i'将返回包含'i''的任何内容 - 实际执行对'ProcessResult(i,..)'的调用时。这是预期的行为。使用'await'将解决这个问题,通过消除lambda *和*简化代码 –
来链接任务。这些可以长时间运行,而等待会暂停当前线程。 – Hector
不,它不会。 'await' *等待*,不会阻止。它相当于'ContinueWith',而不是'Wait'。它使链接更容易*,因为它不需要lambdas和捕获。 –