2

私は非同期/待機が初めてで、タスクのリストを使用してオブジェクトのリストに対して操作を実行するためにそれをいじっています。Linq を使用して、オブジェクトのリストとタスクのリストの両方を生成しました。以下の例は少し不自然に見えますが、実際のコードを簡略化したものです。

示されているようにコードを実行すると、すべてのタスクが完了した後 (待機後)、オブジェクトの Now プロパティは更新されておらず、すべてのタスクのステータスが Running のままであることがわかりました。

.ToList<>() を介してオブジェクトとタスクの両方を実際のリストに変換することで Linq の遅延実行を排除することで、コードが期待どおりに機能することがわかりました (オブジェクトが入力され、タスクはすべて完了するまで実行されます)。

私は Linq の遅延実行に精通していますが、このコードで何が起こっているか (何が起こっていないか) に本当に混乱しています。私はおそらくasync/awaitで初心者の間違いを犯しています...それは何ですか?

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と、シーケンスが再評価され、新しい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"

ToArray一般に、操作の開始など、副作用のある操作を処理する場合は、シーケンスを (使用または類似のものを使用して) "具体化" することをお勧めしますasyncTask.WhenAllはシーケンスを内部的に具体化しますが、再度評価すると (例: ElementAt)、予期しない動作が発生します。

于 2013-06-29T15:30:04.523 に答える