7

Begin/End スタイルのメソッドを使用して非同期ネットワーク I/O を実行しています。(実際には Azure Table Storage に対するクエリですが、それは問題ではないと思います。) を使用してクライアント側のタイムアウトを実装しましたThreadPool.RegisterWaitForSingleObject()。私が知る限り、これはうまく機能しています。

ThreadPool.RegisterWaitForSingleObject()は引数として取るためWaitHandle、I/O 操作を開始してから を実行する必要がありThreadPool.RegisterWaitForSingleObject()ます。これにより、待機を登録する前に I/O が完了する可能性が生じるようです。

簡略化されたコード サンプル:

private void RunQuery(QueryState queryState)
{
    //Start I/O operation
    IAsyncResult asyncResult = queryState.Query.BeginExecuteSegmented(NoopAsyncCallback, queryState);

    //What if the I/O operation completes here? 

    queryState.TimeoutWaitHandle = ThreadPool.RegisterWaitForSingleObject(asyncResult.AsyncWaitHandle, QuerySegmentCompleted, asyncResult, queryTimeout, true);
}

private void QuerySegmentCompleted(object opState, bool timedOut){
    IAsyncResult asyncResult = opState as IAsyncResult;
    QueryState state = asyncResult.AsyncState as QueryState;

    //If the I/O completed quickly, could TimeoutWaitHandle could be null here?
    //If so, what do I do about that?
    state.TimeoutWaitHandle.Unregister(asyncResult.AsyncWaitHandle);
}

これを処理する適切な方法は何ですか?Unregister()AsyncWaitHandle の処理について心配する必要はありますか? もしそうなら、それが設定されるのを待つかなり簡単な方法はありますか?

4

2 に答える 2

4

ええ、あなたも他の誰もがこの問題を抱えています。また、IO が同期的に完了したかどうかは問題ではありません。コールバックと割り当ての間にはまだ競合があります。Microsoft は、RegisteredWaitHandleそのコールバック関数に を自動的に提供する必要がありました。それはすべてを解決したでしょう。まあ、彼らが言うように、後知恵は常に20-20です。

あなたがする必要があるのは、RegisteredWaitHandle変数がnullでなくなるまで読み続けることです。レースは微妙なため、ループが何度も回転することはないため、タイトなループでこれを行うことは問題ありません。

private void RunQuery(QueryState queryState)
{
  // Start the operation.
  var asyncResult = queryState.Query.BeginExecuteSegmented(NoopAsyncCallback, queryState);

  // Register a callback.
  RegisteredWaitHandle shared = null;
  RegisteredWaitHandle produced = ThreadPool.RegisterWaitForSingleObject(asyncResult.AsyncWaitHandle,
    (state, timedout) =>
    {
      var asyncResult = opState as IAsyncResult;
      var state = asyncResult.AsyncState as QueryState;
      while (true)
      {
        // Keep reading until the value is no longer null.
        RegisteredWaitHandle consumed = Interlocked.CompareExchange(ref shared, null, null);
        if (consumed != null)
        {
          consumed.Unregister(asyncResult.AsyncWaitHandle);
          break;
        }
      }
    }, asyncResult, queryTimeout, true);

  // Publish the RegisteredWaitHandle so that the callback can see it.
  Interlocked.CompareExchange(ref shared, produced, null);
}
于 2012-05-24T19:00:44.787 に答える
1

I/O がタイムアウトの前に完了した場合は、登録解除する必要はありません。これは、コールバックのシグナルが完了したためです。実際、 Unregister メソッドのドキュメントを読むと、一度だけ実行し、無関係なメソッドで登録解除していないため、呼び出す必要はまったくないようです。

http://msdn.microsoft.com/en-us/library/system.threading.registeredwaithandle.unregister.aspx

Unregister の実行時にコールバック メソッドが進行中の場合、コールバック メソッドが完了するまで waitObject は通知されません。特に、コールバック メソッドが Unregister を実行する場合、waitObject はそのコールバック メソッドが完了するまで通知されません。

于 2012-05-24T16:58:15.453 に答える