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