Dar*_*ryl 2 c# linq deferred-execution async-await
我是async/await的新手,并且正在修改它以使用任务列表对对象列表执行操作.我使用Linq生成对象列表和任务列表.下面的示例看起来有点人为,但它是我实际代码的简化版本.
我发现当代码如图所示执行时,在所有任务完成之后(等待之后),对象的Now属性都没有更新,并且所有任务的状态仍为Running.
我发现通过.ToList <>()将对象和任务转换为实际列表来消除Linq延迟执行,我的代码按预期工作(填充对象,任务全部运行完成).
我熟悉Linq延迟执行,但我真的很困惑这个代码中发生了什么(不是).我可能在async/await中犯了一个noob错误......它是什么?
private class Foo {
public DateTime Now { get; set; }
}
private void Button_Click( object sender, EventArgs e ) {
PopulateDates();
}
private async void PopulateDates() {
var ordinals = new List<int>() { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, };
var foos = ordinals.Select( o => new Foo() ); //.ToList();
var tasks = foos.Select( f => PopulateDateAsync( f ) ); //.ToList();
await Task.WhenAll( tasks );
var firstNow = foos.ElementAt( 0 ).Now;
var firstTaskStatus = tasks.ElementAt( 0 ).Status;
}
private Task PopulateDateAsync( Foo foo ) {
return Task.Run( () => PopulateDate( foo ) );
}
private void PopulateDate( Foo foo ) {
Thread.Sleep( 2000 );
foo.Now = DateTime.Now;
}
Run Code Online (Sandbox Code Playgroud)
您的问题是由于LINQ的延迟执行.特别是,Task.WhenAll正在等待任务完成.但是,当您调用时ElementAt,将重新评估序列,创建一个新的Foo和Task.
所以,这也行不通:
var ordinals = new List<int>() { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, };
var foos = ordinals.Select( o => new Foo() ); //.ToList();
// Get the first Foo, creating it.
var first = foos.ElementAt(0);
// This gets a *different* Foo. It creates it again.
var other = foos.ElementAt(0);
MessageBox.Show((first == other).ToString()); // Displays "false"
Run Code Online (Sandbox Code Playgroud)
通常,ToArray在处理任何具有副作用的async操作(包括启动操作)时,"重新启动"序列(使用或类似)是个好主意.Task.WhenAll将在内部重新启动您的序列,但如果您再次评估它(例如,ElementAt),您会得到意外的行为.
| 归档时间: |
|
| 查看次数: |
1456 次 |
| 最近记录: |