Noe*_*ams 9 .net c# multithreading
这是代码:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
以下是测试用例:
[TestMethod]
public void TestBeginInvoke()
{
var longOp = new LongOp();
var result = longOp.CallLongOp();
//This can fail
Assert.IsNotNull(result);
}
Run Code Online (Sandbox Code Playgroud)
如果运行此测试用例可能会失败.为什么呢?
关于delegate.BeginInvoke如何工作的文档很少.有没有人有任何他们想分享的见解?
更新 这是一个微妙的竞争条件,在MSDN或其他地方没有详细记录.如接受的答案中所解释的那样,问题是当操作完成时,发出等待句柄的信号,然后执行回调.该信号释放等待的主线程,现在回调执行进入"竞争".Jeffry Richter的建议实现显示了幕后发生的事情:
// 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);
Run Code Online (Sandbox Code Playgroud)
有关解决方案,请参阅Ben Voigt的回答.该实现不会产生第二个等待句柄的额外开销.
Tor*_*och 10
异步操作完成时,将发出ASyncWaitHandle.WaitOne()信号.同时调用CallBack().
这意味着WaitOne()之后的代码在主线程中运行,而CallBack在另一个线程中运行(可能与运行DoLongOp()的线程相同).这会导致竞争条件,其中longOpResult的值在返回时基本上是未知的.
人们可能已经预料到,当CallBack完成时,ASyncWaitHandle.WaitOne()会发出信号,但这不是它的工作方式;-)
您需要另一个ManualResetEvent让主线程等待CallBack设置longOpResult.
正如其他人所说,result.WaitOne只是意味着目标BeginInvoke已经完成,而不是回调.所以只需将后处理代码放入BeginInvoke委托中即可.
//Call the asynchronous operation
Action callAndProcess = delegate { longOpDelegate(); Callafter(); };
IAsyncResult result = callAndProcess.BeginInvoke(r => callAndProcess.EndInvoke(r), null);
//Wait for it to complete
result.AsyncWaitHandle.WaitOne();
//return result saved in Callafter
return longOpResult;
Run Code Online (Sandbox Code Playgroud)