IAsyncResultを明示的に実装する
-
03-07-2019 - |
質問
私は一般的に、部分的にインターフェースを実装することに慎重です。ただし、 IAsyncResult
は、いくつかのまったく異なる使用パターンをサポートしているため、少し特殊なケースです。 AsyncWaitHandle
を使用して、単に EndInvoke
を呼び出すのではなく、 AsyncState
/ AsyncCallback
パターンを使用/表示する頻度>、またはポーリング IsCompleted
(yuck)?
関連する質問: ThreadPool WorkItemの検出完了/完了待ち。
このクラスを検討してください(非常に近似した、ロックが必要です):
public class Concurrent<T> {
private ManualResetEvent _resetEvent;
private T _result;
public Concurrent(Func<T> f) {
ThreadPool.QueueUserWorkItem(_ => {
_result = f();
IsCompleted = true;
if (_resetEvent != null)
_resetEvent.Set();
});
}
public WaitHandle WaitHandle {
get {
if (_resetEvent == null)
_resetEvent = new ManualResetEvent(IsCompleted);
return _resetEvent;
}
public bool IsCompleted {get; private set;}
...
WaitHandle ( IAsyncResult
のドキュメントで説明されているように遅延作成)と IsCompleted
がありますが、賢明な実装は見当たりません AsyncState
( {return null;}
?) IAsyncResult
を実装するのは理にかなっていますか? Parallel Extensionsライブラリの Task
は IAsyncResult
を実装しますが、暗黙的に実装されるのは IsCompleted
だけです。
解決
- 私の経験では、先に待機したりコールバックしたりせずにEndInvokeを呼び出すだけではほとんど役に立ちません
- コールバックを提供するだけでは不十分な場合があります。クライアントが一度に複数の操作を待機する場合があるためです(WaitAny、WaitAll)
- IsCompletedをポーリングしたことはありません。そのため、IsCompletedの実装を保存できますが、非常に単純なので、クライアントを驚かせる価値はないようです。
したがって、非同期的に呼び出し可能なメソッドの合理的な実装は、完全に実装されたIAsyncResultを実際に提供する必要があります。
ところで、しばしばIAsyncResultを自分で実装する必要はなく、Delegate.BeginInvokeによって返されたものを返すだけです。例については、System.IO.Stream.BeginReadの実装を参照してください。
他のヒント
いくつか質問があるようです。それらを個別に処理しましょう
WaitHandleの遅延作成
はい、これが最も正しいアプローチです。スレッドセーフな方法でこれを行う必要がありますが、怠zyな方法です。
しかし、トリックはWaitHandleを破棄することです。 WaitHandleはIDisposableのベースであり、タイムリーに破棄する必要があります。 IAsycResultのドキュメントはこのケースをカバーしていません。これを行う最良の方法は、EndInvokeにあります。 BeginInvokeのドキュメントには、すべてのBeginInvokeについて、対応するEndInvoke(またはBeginRead / EndRead)が必要であることが明示的に記載されています。これは、WaitHandleを処分するのに最適な場所です。
AsyncStateの実装方法
IAsyncResultを返す標準BCL APIを見ると、それらのほとんどは状態パラメーターを取ります。これは通常、AsyncStateから返される値です(例については、ソケットAPIを参照してください)。 IAsyncResultを返すAPI BeginInvokeスタイルAPIのオブジェクトとして型指定された状態変数を含めることをお勧めします。必要ではありませんが、良い習慣です。
状態変数が存在しない場合、nullを返すことは許容されます。
IsCompleted API
これは、IAsyncResultを作成する実装に大きく依存します。しかし、はい、あなたはそれを実装する必要があります。