为什么这个AsyncCallback测试有时会失败
本文关键字:失败 测试 AsyncCallback 为什么 | 更新日期: 2023-09-27 18:11:05
我有以下类,它试图充当一个简单的异步操作:
public class AsyncLineWriter
{
private delegate void SynchronousWriteLineDelegate(string message);
private SynchronousWriteLineDelegate DoWriteLine;
private void SynchronousWriteLine(string message)
{
Console.WriteLine(message);
}
public AsyncLineWriter()
{
DoWriteLine = new SynchronousWriteLineDelegate(SynchronousWriteLine);
public IAsyncResult BeginWriteLine(string message, AsyncCallback callback, object state)
{
return DoWriteLine.BeginInvoke(message,callback,state);
}
public void EndWriteLine(IAsyncResult asyncResult)
{
DoWriteLine.EndInvoke(asyncResult);
}
}
下面的单元测试间歇性失败,但我不明白竞态条件在哪里:
[TestMethod]
public void Callback_is_called()
{
// Arrange
AsyncLineWriter lineWriter = new AsyncLineWriter();
object state = new object();
object callbackState = null;
AsyncCallback callback = (r) =>
{
callbackState = r.AsyncState;
};
// Act
IAsyncResult asyncResult = lineWriter.BeginWriteLine("test", callback, state);
lineWriter.EndWriteLine(asyncResult);
// Assert
Assert.AreSame(state, callbackState);
}
在此模式中,回调在线程池线程上运行,您应该从回调中调用EndInvoke
。
EndInvoke
不等待回调完成(因为这会导致死锁),因此在回调和测试方法之间存在竞争。
编辑:等待句柄也可以在回调完成之前设置。试试这个:
[TestMethod]
public void Callback_is_called()
{
// Arrange
var lw = new AsyncLineWriter();
object state = new object();
object callbackState = null;
var mre = new ManualResetEvent( false );
AsyncCallback callback = r =>
{
callbackState = r.AsyncState;
lw.EndWriteLine( r );
mre.Set();
};
// Act
var ar = lw.BeginWriteLine( "test", callback, state );
mre.WaitOne();
// Assert
Assert.AreSame( state, callbackState );
}
如前所述,在测试成功的情况下,您只是幸运地发现线程以这样一种方式交错,即在调用EndInvoke
之前调用回调。正确的APM模式是在回调中调用EndWriteLine,这意味着你必须将AsyncLineWriter
作为状态的一部分传递给BeginInvoke
方法。
IAsyncResult
WaitHandle
发出信号后发生。并不是回调没有被调用它只是在检查发生后被调用。
AsyncLineWriter lineWriter = new AsyncLineWriter();
Object myState = new Object();
object[] state = new object[2];
state[0] = lineWriter;
state[1] = myState;
object callbackState = null;
ManualResetEvent evnt = new ManualResetEvent(false);
AsyncCallback callback = (r) =>
{
Object[] arr = (Object[])r.AsyncState;
LineWriter lw = (LineWriter)arr[0];
Object st = arr[1];
callbackState = st;
lw.EndWriteLine(r);
evnt.Set();
};
// Act
IAsyncResult asyncResult = lineWriter.BeginWriteLine("test", callback, state);
//asyncResult.AsyncWaitHandle.WaitOne(); -- callback can still happen after this!
evnt.WaitOne();
//Assert
Assert.AreSame(myState, callbackState);