4

この質問に対する Andreas Huber の回答Concurrent<T>により、ThreadPool の代わりに非同期デリゲートを使用して実装するというアイデアが得られました。AsyncCallbackただし、が に渡されたときに何が起こっているのかを理解するのが難しくなっています。BeginInvoke特に、複数のスレッドが にアクセスしている場合はそうIAsyncResultです。残念ながら、このケースは MSDN や私が見つけたどこでもカバーされていないようです。さらに、私が見つけたすべての記事は、クロージャーとジェネリックが利用可能になる前に書かれたか、単にそのように見えます。いくつかの質問があります (答えが正しいことを願っていますが、がっかりすることは覚悟しています):

1) クロージャを AsyncCallback として使用すると違いはありますか?
(うまくいかないことを願っています)
2) スレッドが で待機している場合、 a) コールバックが開始する前、または b) 終了後にAsyncWaitHandle通知されますか? (できれば b) 3) コールバックが実行されている間、何が返されますか? 私が見ることができる可能 性: b) ; c)コールバックが EndInvoke を呼び出す前、後。 (願わくば b または c) 4)呼び出された後にスレッドが待機すると、スローされますか? (そうではないことを願っていますが、私はそう期待しています)。


IsCompleted
truefalsefalsetrue

DisposedObjectExceptionAsyncWaitHandleEndInvoke

答えが私が望んでいるものであれば、これはうまくいくようです:

public class Concurrent<T> { 
    private IAsyncResult _asyncResult;
    private T _result;

    public Concurrent(Func<T> f) { // Assume f doesn't throw exceptions
        _asyncResult = f.BeginInvoke(
                           asyncResult => {
                               // Assume assignment of T is atomic
                               _result = f.EndInvoke(asyncResult); 
                           }, null);
    }

    public T Result {
        get {
            if (!_asyncResult.IsCompleted)
                // Is there a race condition here?
                _asyncResult.AsyncWaitHandle.WaitOne();
            return _result;  // Assume reading of T is atomic
        }
    ...

質問 1 から 3 への回答が私が望んでいるものである場合、私が見る限り、ここにレース条件はないはずです。

4

2 に答える 2

2

私は最近非同期呼び出しを調べています。尊敬されている著者のジェフリー・リッチターによるIAsyncResultの実装例を含む記事へのポインターを見つけました。この実装を研究することで、非同期呼び出しがどのように機能するかについて多くを学びました。

System.Runtime.Remoting.Messaging.AsyncResultまた、特に関心のあるソースコードをダウンロードして調べることができるかどうかも確認できます。VisualStudioでこれを行う方法の説明へのリンクは次のとおりです。

JaredParの良い答えに少し追加するには...

1:AsyncCallback型の変数に割り当てることができるクロージャを定義すると(IAsyncResultを受け取り、voidを返す)、クロージャがそのデリゲートとして機能することを期待するのと同じように機能するはずですが、そこにあるかどうかはわかりませんスコープの問題である可能性があります。元のローカルスコープは、コールバックが呼び出されるずっと前に戻っているはずです(これにより、非同期操作になります)。したがって、ローカル(スタック)変数への参照とその動作について注意してください。メンバー変数を参照することは問題ないと思います。

2:あなたのコメントから、あなたはこれに対する答えを誤解したかもしれないと思います。Jeffrey Richterの実装例では、コールバックが呼び出される前に待機ハンドルが通知されます。あなたがそれについて考えるならば、それはこのようでなければなりません。コールバックを呼び出すと、実行の制御が失われます。コールバックメソッドが例外をスローするとします。実行により、コールバックを呼び出したメソッドを超えて巻き戻され、後で待機ハンドルに信号を送ることができなくなります。したがって、コールバックが呼び出される前に、待機ハンドルを通知する必要があります。また、コールバックが戻った後にのみ待機ハンドルに信号を送る場合よりも、この順序で実行した方が時間的にはるかに近くなります。

3:JaredParが言うように、IsCompletedは、コールバックの前、および待機ハンドルが通知されるtrue 前に戻る必要があります。IsCompletedがfalseの場合、呼び出しがEndInvokeブロックされることを期待し、(コールバックの場合と同様に)待機ハンドルの全体的なポイントは、結果の準備ができてブロックされないことを知ることであるため、これは理にかなっています。したがって、最初にIsCompletedがに設定されtrue、次に待機ハンドルが通知され、次にコールバックが呼び出されます。ジェフリー・リッチターの例がそれをどのように行うかをご覧ください。 ただし、これらの3つのメソッド(ポーリング、待機ハンドル、コールバック)が完了を検出する可能性がある順序については、予想とは異なる順序で実装できるため、想定を避ける必要があります。

4:興味のある実装のフレームワークソースコードにデバッグすることで答えが見つかるかもしれないことを除いて、私はそこであなたを助けることはできません。または、実験を考えて調べることもできます...または、適切な実験を設定してフレームワークソースにデバッグし、確実に確認することもできます。

于 2009-09-02T08:31:35.883 に答える
2

質問1

問題の一部は誤解だと思います。IAsyncResult は、明示的に 1 つのスレッドに渡さない限り、複数のスレッドからアクセスされません。BCL の mos Begin*** スタイル API の実装を見ると、Begin*** または End*** 呼び出しが実際に発生するスレッドからのみ IAsyncResult が作成および破棄されることがわかります。

質問2

AsyncWaitHandle は、操作が 100% 完了した後に通知する必要があります。

質問 3

IsCompleted は、基になる操作が完了すると true を返す必要があります (これ以上の作業は必要ありません)。IsComplete を表示する最良の方法は、値が

  1. true -> End*** を呼び出すとすぐに戻ります
  2. false -> Callind End*** は一定期間ブロックされます

質問 4

これは実装に依存します。ここで一概に答えることはできません。

サンプル

別のスレッドでデリゲートを簡単に実行し、終了時に結果にアクセスできる API に興味がある場合は、私のRantPack Utility Libraryをチェックしてください。ソース形式とバイナリ形式で利用できます。デリゲートの同時実行を可能にする、完全に肉付けされた Future API があります。

さらに、この投稿のほとんどの質問に対応する IAsyncResult の実装があります。

于 2009-01-02T14:46:45.567 に答える