BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ ニュース .NET 6:非同期の改善

.NET 6:非同期の改善

原文(投稿日:2021/04/15)へのリンク

.NET 6でのAPIの100を超える変更の中には、非同期コードの利用をより簡単かつ安全にするために設計されたいくつかの機能がある。ハイライトをいくつか紹介する。

新しいWaitAsyncメソッド

理想は、すべての非同期機能が操作をキャンセルする機能を提供することであるが、提供されない場合もある。そのために、3つの新しいWaitAsyncメソッドTaskTask<TResult>に追加された。

public Task WaitAsync(CancellationToken cancellationToken);
public Task WaitAsync(TimeSpan timeout);
public Task WaitAsync(TimeSpan timeout, CancellationToken cancellationToken);

このシグネチャーが示すように、これらのメソッドはタイムアウトまたはCancellationToken、あるいはその両方を引数として受ける。これらのいずれかを使用して、非同期操作の完了待ちを中止できる。これは、操作自体を中止することと同じではないことに注意してください。呼び出し元のコードが完了を待たなくなったとしても操作は続行される場合がある。

ちょっとした雑学だが、Microsoftはintミリ秒の形式のタイムアウト値を間違いと見なすようになった。今後、すべての期間ベースのタイムアウトは、TimeSpanの観点からのみ表現する必要がある。

再利用可能なCancellationTokenSource

Webリクエストなどの外部でトリガーされる操作が開始されると、多くの場合、CancellationTokenSourceを作成する必要がある。これにより、リクエスターがリクエストをキャンセルしたり、接続が切断されたりした場合に、リクエストハンドラーを中止できる。ほとんどの場合、リクエストはキャンセルされない。つまり、CancellationTokenSourceは起動されない。

パフォーマンスを向上させるために、フレームワーク開発者は、CancellationTokenSourceを新しい操作に再利用したいと考えている。しかし、現在、リンクされている古いCancellationTokenを何が保持しているのかを知る術がないため、再利用できない。操作がキャンセルされると、奇妙なエラーが発生する可能性がある。リサイクルされたCancellationTokenSourceをまったく別の操作と共有するためである。

新しいCancellationTokenSource.TryReset操作は、この問題を修正している。すべての古いCancellationTokenオブジェクトをCancellationTokenSourceから切断する。このように、新しい操作をキャンセルしても、前の操作に影響を与えることはできません。古いCancellationTokenオブジェクトはそれでも存在するが、リサイクルされたCancellationTokenSourceからメッセージを受信することはない。

これは、起動しないCancellationTokenSourceでのみ機能するため、TryResetと呼ばれる。CancellationTokenSourceが実際にキャンセルされると、リサイクルされない場合がある。そのため、使用パターンは次のようになる。

if (!cts.TryReset())
    cts = new CancellationTokenSource();

キャンセルイベント

いつキャンセルが要求されたかを検出する方法の1つは、CancellationToken.Registerを呼び出して、それに呼び出す権限を委譲することである。まれに、この委譲は、使用された元のCancellationTokenにアクセスする必要がある。

CancellationToken.Registerの新しいオーバーロードは、次の機能を提供する。

public CancellationTokenRegistration Register<T>(Action<T, CancellationToken> callback, T state);
public CancellationTokenRegistration UnsafeRegister<T>(Action<T, CancellationToken> callback, T state);

UnsafeRegisterのドキュメントは完全ではないが、古いバグレポートのコメントには次のように書かれている。

CancellationToken.Registerは、現在のExecutionContextをキャプチャし、呼び出された場合にコールバックを呼び出すためにそれを使用する。これは一般的に望ましいことであり、正しいデフォルト動作である。しかし、コールバックがECを気にしないことが確実にわかっている場合(たとえば、サードパーティのコードを呼び出さない)、代わりにUnsafeRegisterを使用できる(3.0で新しく追加された)。UnsafeRegisterは、Capturenullを返したかのように、ExecutionContextのキャプチャをスキップする。

より簡単な実行コンテキストの復元

Ben Adams氏によると、「それ自体が非同期ではないメソッドを返すタスクを待つことは、AsyncLocal/ExecutionContextの変更を元に戻すことを保証するものではない」。その結果、診断が難しいバグが発生する可能性がある。この例では、Propagatedヘッダーが以前の非同期ミドルウェアなしで混同されKestrelが、AsyncLocalによる同じHTTP/1.1接続上のリクエスト間でのコンテキストデータ流出を引き起こす

ExecutionContext.Capture新しいExecutionContext.Restore関数と組み合わせることで、この問題を回避できる。

このシリーズの以前のレポートは、以下のリンクを参照してください。

この記事に星をつける

おすすめ度
スタイル

BT