的下面是代码:IAsyncResult.AsyncWaitHandle.WaitOne()提前完成回调
class LongOp
{
//The delegate
Action longOpDelegate = LongOp.DoLongOp;
//The result
string longOpResult = null;
//The Main Method
public string CallLongOp()
{
//Call the asynchronous operation
IAsyncResult result = longOpDelegate.BeginInvoke(Callback, null);
//Wait for it to complete
result.AsyncWaitHandle.WaitOne();
//return result saved in Callback
return longOpResult;
}
//The long operation
static void DoLongOp()
{
Thread.Sleep(5000);
}
//The Callback
void Callback(IAsyncResult result)
{
longOpResult = "Completed";
this.longOpDelegate.EndInvoke(result);
}
}
下面是测试情况:
[TestMethod]
public void TestBeginInvoke()
{
var longOp = new LongOp();
var result = longOp.CallLongOp();
//This can fail
Assert.IsNotNull(result);
}
如果这是运行测试用例可能会失败。为什么呢?
很少有关于delegate.BeginInvoke如何工作的文档。有没有人有任何见解他们想分享?
更新 这是一个微妙的竞争条件,MSDN或其他地方没有很好的记录。正如接受的答案中所解释的那样,问题是当操作完成时,等待手柄会发出信号,然后执行回调。信号释放等待的主线程,现在回调执行进入“比赛”。 Jeffry Richter's suggested implementation显示发生了什么幕后:
// If the event exists, set it
if (m_AsyncWaitHandle != null) m_AsyncWaitHandle.Set();
// If a callback method was set, call it
if (m_AsyncCallback != null) m_AsyncCallback(this);
有关解决方案请参阅本福格特的答案。该实现不会导致第二个等待句柄的额外开销。
删除回调并重试。 – jgauffin 2010-11-04 17:22:56
@jgauffin,如果你注意到这个问题不是问“我如何得到这个工作?”显然这是一个人为的例子。 – 2010-11-04 17:39:39
您的问题是:“如果运行该测试用例可能会失败,为什么?”。我*做了*回答。因为您尝试混合处理异步操作的两种非常不同的方式。 – jgauffin 2010-11-04 17:57:29