C#非同步呼叫是個好東西,省卻無數麻煩。然而最近發現如果被非同步呼叫的方法內有時間被觸發,並且非同步呼叫結束回呼函數中執行序列化操作的時候就會出現結束回呼函數被反覆調用兩次的情況。具體代碼如下(從MSDN執行個體代碼中修改而來)
using System;
using System.Threading;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Remoting.Messaging;
namespace test
{
[Serializable]
public class AsyncDemo
{
public delegate void TestRaiseEventHandler(object sender, EventArgs e);
public event TestRaiseEventHandler TestRaise;
protected virtual void OnTestRaiseEventHandler()
{
if (TestRaise != null)
{
TestRaise(this, null);
}
}
// 將被非同步呼叫的方法
//
public string TestMethod(int callDuration, out int threadId)
{
Console.WriteLine("Test method begins.");
OnTestRaiseEventHandler();
Thread.Sleep(callDuration);
threadId = AppDomain.GetCurrentThreadId();
return "MyCallTime was " + callDuration.ToString();
}
// 序列化操作
public void Serialize(string filename)
{
Serialize(filename, this);
}
public static void Serialize(string filename, AsyncDemo async)
{
FileStream fs = new FileStream(filename, FileMode.Create);
BinaryFormatter formatter = new BinaryFormatter();
try
{
formatter.Serialize(fs, async);
}
finally
{
fs.Close();
}
}
}
// 用來執行非同步呼叫的代理
public delegate string AsyncDelegate(int callDuration, out int threadId);
public class AsyncMain
{
private static int threadId;
static void Main(string[] args)
{
// 建立測試類別
AsyncDemo ad = new AsyncDemo();
// 添加事件.(1)
ad.TestRaise += new test.AsyncDemo.TestRaiseEventHandler((new AsyncMain()).ad_TestRaise);
// 建立非同步方法呼叫的代理
AsyncDelegate dlgt = new AsyncDelegate(ad.TestMethod);
// 開始執行非同步呼叫
IAsyncResult ar = dlgt.BeginInvoke(1000,
out threadId,
new AsyncCallback(CallbackMethod),
ad );
// 等待調用完成
Console.WriteLine("Press Enter to close application.");
Console.ReadLine();
}
// 事件響應函數
void ad_TestRaise(object sender, EventArgs e)
{
Console.WriteLine("delegate call");
}
// 非同步呼叫完成時的回呼函數
static void CallbackMethod(IAsyncResult ar)
{
// 得到非同步方法呼叫的代理
AsyncDelegate dlgt = (AsyncDelegate) (ar as AsyncResult).AsyncDelegate;
Console.WriteLine("begin EndInvoke");
string ret = dlgt.EndInvoke(out threadId, ar);
Console.WriteLine("The call executed on thread {0}, with return value \"{1}\".", threadId, ret);
// 執行序列化操作(2)
AsyncDemo demo = ar.AsyncState as AsyncDemo;
demo.Serialize("Test.dat");
}
}
}
上述代碼輸出如下:
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
Press Enter to close application.
Test method begins.
delegate call
begin EndInvoke
The call executed on thread 3736, with return value "MyCallTime was 1000".
begin EndInvoke
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
如上代碼可見,當同時具備兩個條件時:
(1)非同步方法呼叫中有事件發生並被客戶代碼響應,
(2)非同步作業結束時執行序列化操作
CallbackMethod方法將被執行兩次,且序列化操作不能正常完成,第二次執行的時候終止於EndInvoke語句。
在網路上查詢似乎並沒有關於這個問題的描述,不知道是否有人遇到過相似的問題?