C# 如果操作首先完成,则使用RegisterWaitForSingleObject
我正在使用Begin/End风格的方法进行一些异步网络I/O。(这实际上是对Azure表存储的查询,但我认为这并不重要。)我已经使用C# 如果操作首先完成,则使用RegisterWaitForSingleObject,c#,.net,multithreading,asynchronous,C#,.net,Multithreading,Asynchronous,我正在使用Begin/End风格的方法进行一些异步网络I/O。(这实际上是对Azure表存储的查询,但我认为这并不重要。)我已经使用ThreadPool.RegisterWaitForSingleObject()实现了客户端超时。据我所知,这很好用 由于ThreadPool.RegisterWaitForSingleObject()将WaitHandle作为参数,因此我必须开始I/O操作,然后执行ThreadPool.RegisterWaitForSingleObject()。这似乎引入了一种可
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的注销()?如果是这样,是否有一种相当简单的方法等待设置 如果I/O在超时之前完成,则无需取消注册,因为正是完成发出回调信号。事实上,在阅读Unregister方法的文档时,似乎完全没有必要调用它,因为您只执行了一次,并且您没有在不相关的方法中注销
如果在执行Unregister时回调方法正在进行,则在回调方法完成之前不会通知waitObject。特别是,如果回调方法执行Unregister,则在该回调方法完成之前不会通知waitObject
是的,你和其他人都有这个问题。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);
}
您是否尝试插入“代码>线程”?在中间睡眠“/CODE”,以允许I/O操作的时间完成,看看会发生什么?我没有。我想我只见过这种情况发生过3-4次,然后只出现在重载生产机器上。我不想开始在我的实际代码中添加Sleep()调用。当然,在您的开发环境中测试它。当你说你只见过这种情况发生3-4次,你看到了什么?随机未解释的NullPointerException?确切地说,是Unregister()行上的NullPointerException。既然它提供了这样一个选项,为什么不在BeginExecuteSegmented
中设置回调?否则,我看不出如何避免这种可能的争用情况。我想引用的文档是说它不会向我的操作的WaitHandle发出信号。它似乎没有提到我从ThreadPool获得的RegisteredWaitHandle。但这太令人困惑了,所以我可能错了。关于RegisterWaitForSingleObject()的文档明确指出,您应该始终注销(),因此我认为我确实需要在某个地方这样做@breischl QuerySegmentCompleted仅作为来自RegisterWaitForSingleObject的回调调用?顺便说一句,这是一个很好的问题。如果state.TimeoutWaitHandle为null,您可能会将WaitHandle的注销留给非最佳的终结器线程。同时,文件上说你应该注销,这在你的情况下是不可能的。我想我明白你的意思了。是否真的需要两个RegisteredWaitHandle变量和CompareExchange()?既然指针赋值是一个原子操作,我就不能检查回调中产生的变量了吗?@breischl:嗯,你需要一个内存屏障,这样每次迭代都能“重新读取”变量。您可能可以忽略Interlocked.CompareExchange
调用,因为我确信RegisteredWaitHandle.Unregistered
也会生成内存障碍。但是,就我个人而言,我会坚持这种模式,因为是互锁的。compareeexchange
使它变得明确。将生成的声明为volatile
不会有同样的效果吗?@breischl:当然,除了不能将局部变量声明为volatile
。但是,你的想法是对的。当然,您可以重新构造代码,以便使用volatile
。有很多有效的方法可以做到这一点。啊,对了,当。顺便说一下,谢谢你回答我的许多后续问题。再加两个(我想):一个Thread.volatireRead()
就足够了吗?另外,是否值得执行Thread.Yield()
或while(true)
循环的内部操作?