的下面是代碼: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