7

我正在使用 Begin/End 样式方法进行一些异步网络 I/O。(它实际上是针对 Azure 表存储的查询,但我认为这并不重要。)我已经使用ThreadPool.RegisterWaitForSingleObject(). 据我所知,这工作正常。

因为ThreadPool.RegisterWaitForSingleObject()需要 aWaitHandle作为参数,所以我必须开始 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变量,直到它不再为空。可以在紧凑的循环中执行此操作,因为比赛非常微妙,循环不会旋转很多次。

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 回答