RegisterWaitForSingleObject

RegisterWaitForSingleObject

我正在使用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);
}

处理此问题的正确方法是什么?我是否还需要担心AsyncWaitHandle的Unregister()吗?如果是这样,是否有一种相当简单的方法等待设置?

最佳答案

是的,您和其他所有人都有这个问题。 IO是否同步完成也无关紧要。回调和分配之间仍然存在竞争。 Microsoft应该已经自动向该回调函数提供了RegisteredWaitHandle。那将解决所有问题。哦,好吧,事后看来,他们总是说20-20。

您需要做的就是继续阅读RegisteredWaitHandle变量,直到不再为空为止。可以在一个紧密的循环中进行此操作,因为比赛非常细微,以至于循环不会旋转很多次。

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);
}

10-04 16:37