Tirion

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
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(
thisnull);
            }

        }

        
// 将被异步调用的方法
        
//
        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语句。
在网络上查询似乎并没有关于这个问题的描述,不知道是否有人遇到过相似的问题?
posted on 2005-02-21 02:01  Findekano  阅读(1976)  评论(3编辑  收藏  举报