1. ホーム
  2. c#

[解決済み】linq selectで非同期await。

2022-04-03 04:15:08

質問

既存のプログラムを修正する必要があり、以下のコードが含まれています。

var inputs = events.Select(async ev => await ProcessEventAsync(ev))
                   .Select(t => t.Result)
                   .Where(i => i != null)
                   .ToList();

しかし、これは私には非常に奇妙に思えます。 asyncawait をセレクトしてください。によると この答え by Stephen Cleary それらを削除することができるはずです。

次に、2番目の Select で、その結果を選択します。これはタスクが全く非同期ではなく、同期的に実行されることを意味しませんか(無駄に労力がかかる)、それともタスクは非同期的に実行され、それが完了したら残りのクエリが実行されるのでしょうか?

に従って、上記のコードを次のように書けばよいのでしょうか? Stephen Clearyによる別の回答 :

var tasks = await Task.WhenAll(events.Select(ev => ProcessEventAsync(ev)));
var inputs = tasks.Where(result => result != null).ToList();

と、このように完全に同じなのでしょうか?

var inputs = (await Task.WhenAll(events.Select(ev => ProcessEventAsync(ev))))
                                       .Where(result => result != null).ToList();

このプロジェクトに取り組んでいる間、最初のコードサンプルを変更したいのですが、(明らかに動作している)非同期コードを変更するのはあまり気が進みません。もしかしたら、私は無駄に心配しているだけで、3つのコードサンプルはすべて全く同じことを行っているのでしょうか?

ProcessEventsAsyncはこのような感じです。

async Task<InputResult> ProcessEventAsync(InputEvent ev) {...}

解決方法は?

var inputs = events.Select(async ev => await ProcessEventAsync(ev))
                   .Select(t => t.Result)
                   .Where(i => i != null)
                   .ToList();

まず、asyncとawaitがselectで使われていることです。Stephen Clearyの回答によると、これらを削除することができるはずです。

の呼び出しは Select が有効です。この2行は基本的に同じです。

events.Select(async ev => await ProcessEventAsync(ev))
events.Select(ev => ProcessEventAsync(ev))

(からの同期例外の投げ方については、若干の違いがあります。 ProcessEventAsync しかし、このコードの文脈では全く重要ではありません)。

<ブロッククオート

そして、結果を選択する2番目のSelect。これは、タスクが非同期ではなく、同期で実行されることを意味しませんか(無駄に労力がかかる)。それとも、タスクは非同期で実行され、それが完了したら残りのクエリが実行されるのでしょうか?

クエリがブロック化されているということです。ですから、実際には非同期ではありません。

分解してみる。

var inputs = events.Select(async ev => await ProcessEventAsync(ev))

は、まず各イベントに対して非同期処理を開始します。次にこの行。

                   .Select(t => t.Result)

は、それらの操作が一度に完了するのを待ちます (最初に最初のイベントの操作を待ち、次に次のイベントの操作を待ち、さらに次のイベントの操作を待つ、といった具合です)。

この部分は私が気にしない部分である。なぜなら、ブロックしてしまうし、例外が発生した場合、それを AggregateException .

<ブロッククオート

と、このように完全に同じなのでしょうか?

var tasks = await Task.WhenAll(events.Select(ev => ProcessEventAsync(ev)));
var inputs = tasks.Where(result => result != null).ToList();

var inputs = (await Task.WhenAll(events.Select(ev => ProcessEventAsync(ev))))
                                       .Where(result => result != null).ToList();

はい、この2つの例は等価です。どちらもすべての非同期オペレーションを開始します ( events.Select(...) )、そしてすべての処理が任意の順序で完了するのを非同期に待ちます ( await Task.WhenAll(...) )、そして残りの作業を進める( Where... ).

これらの例は、いずれも元のコードとは異なっています。元のコードはブロック化されており、例外を AggregateException .