2

我是 async/await 的新手,我正在修改它以使用任务列表对对象列表执行操作。我使用 Linq 生成对象列表和任务列表。下面的例子看起来有点做作,但它是我实际代码的简化版本。

我发现当代码如图所示执行时,在所有任务完成后(等待之后),对象的Now属性都没有更新,所有任务的状态仍然是Running。

我发现通过通过 .ToList<>() 将对象和任务都转换为实际列表来消除 Linq 延迟执行,我的代码按预期工作(填充了对象,任务全部运行到完成)。

我熟悉 Linq 延迟执行,但我真的对这段代码中发生的(不是)事情感到困惑。我可能在异步/等待方面犯了一个菜鸟错误……它是什么?

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;
}
4

1 回答 1

6

您的问题是由于 LINQ 的延迟执行造成的。尤其Task.WhenAll是适当地等待任务完成。但是,当您调用 时ElementAt,将重新评估序列,创建一个新的Fooand 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"

ToArray通常,在处理任何具有副作用的操作(包括启动操作)时,“具体化”您的序列(使用或类似)是一个好主意asyncTask.WhenAll将在内部具体化您的序列,但如果您再次评估它(例如,ElementAt),您会得到意想不到的行为。

于 2013-06-29T15:30:04.523 回答