异步Lambda中的参数 [英] Parameters in asynchronous lambdas
问题描述
我试图同时运行多个任务,但遇到一个我似乎无法理解或解决的问题.
I am trying to run several tasks at the same time and I came across an issue I can't seem to be able to understand nor solve.
我曾经有过这样的功能:
I used to have a function like this :
private void async DoThings(int index, bool b) {
await SomeAsynchronousTasks();
var item = items[index];
item.DoSomeProcessing();
if(b)
AVolatileList[index] = item; //volatile or not, it does not work
else
AnotherVolatileList[index] = item;
}
我想使用Task.Run()
在for
循环中调用.但是我找不到将参数发送到此Action<int, bool>
的方法,每个人都建议在类似情况下使用lambda:
That I wanted to call in a for
loop using Task.Run()
. However I could not find a way to send parameters to this Action<int, bool>
and everyone recommends using lambdas in similar cases:
for(int index = 0; index < MAX; index++) { //let's say that MAX equals 400
bool b = CheckSomething();
Task.Run(async () => {
await SomeAsynchronousTasks();
var item = items[index]; //here, index is always evaluated at 400
item.DoSomeProcessing();
if(b)
AVolatileList[index] = item; //volatile or not, it does not work
else
AnotherVolatileList[index] = item;
}
}
我认为在lambdas中使用局部变量会捕获"它们的值,但看起来并没有.它将始终采用index的值,就好像该值将在for
循环结束时捕获一样.在每次迭代中,index
变量在lambda中的计算结果为400,因此我当然得到了IndexOutOfRangeException
400倍(items.Count
实际上是MAX
).
I thought using local variables in lambdas would "capture" their values but it looks like it does not; it will always take the value of index as if the value would be captured at the end of the for
loop. The index
variable is evaluated at 400 in the lambda at each iteration so of course I get an IndexOutOfRangeException
400 times (items.Count
is actually MAX
).
我真的不确定这里发生了什么(尽管我对此很好奇),我也不知道该怎么做才能达到的目标.欢迎任何提示!
I am really not sure about what is happening here (though I am really curious about it) and I don't know how to do what I am trying to achieve either. Any hints are welcome!
推荐答案
制作索引变量的本地副本:
Make a local copy of your index variable:
for(int index = 0; index < MAX; index++) {
var localIndex = index;
Task.Run(async () => {
await SomeAsynchronousTasks();
var item = items[index];
item.DoSomeProcessing();
if(b)
AVolatileList[index] = item;
else
AnotherVolatileList[index] = item;
}
}
这是由于C#进行for
循环的方式:只有一个index
变量被更新,并且您所有的lambda都捕获了相同的变量(使用lambda,变量是捕获的,而不是值).
This is due to the way C# does a for
loop: there is only one index
variable that is updated, and all your lambdas are capturing that same variable (with lambdas, variables are captured, not values).
作为旁注,我建议您:
- 避免使用
async void
.您永远无法知道async void
方法何时完成,并且它们具有难以处理的错误语义. -
await
您的所有异步操作.即,不要忽略Task.Run
返回的任务.将Task.WhenAll
等用于await
.这样可以传播异常.
- Avoid
async void
. You can never know when anasync void
method completes, and they have difficult error handling semantics. await
all of your asynchronous operations. I.e., don't ignore the task returned fromTask.Run
. UseTask.WhenAll
or the like toawait
for them. This allows exceptions to propagate.
例如,这是使用WhenAll
的一种方法:
For example, here's one way to use WhenAll
:
var tasks = Enumerable.Range(0, MAX).Select(index =>
Task.Run(async () => {
await SomeAsynchronousTasks();
var item = items[localIndex];
item.DoSomeProcessing();
if(b)
AVolatileList[localIndex] = item;
else
AnotherVolatileList[localIndex] = item;
}));
await Task.WhenAll(tasks);
这篇关于异步Lambda中的参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!