学习笔记:同步和异步调用delegate

每次我们创建一个delegate类型的时候,系统会为我们创建一个继承自MulticastDelegate的类。假设我们创建一个如下的delegate:
public delegate int BinaryOp(int x, int y);
public sealed class BinaryOp : System.MulticastDelegate
{
    public BinaryOp(object target, uint functionAddress);
    public void Invoke(int x, int y);
    public IAsyncResult BeginInvoke(int x, int y, AsyncCallback cb, object state);
    public int EndInvoke(IAsyncResult result);
}
实际上,当我们通过声明一个delegate并调用该方法的时候,我们是调用Invoke方法。系统在执行完该方法之后再返回,整个过程是一个同步调用,系统在执行delegate代码的时候会等待该部分执行完再继续,而且所有这些代码的执行是在同一个线程里面。
如果我们要通过异步调用来执行delegate的话,则需要使用BeginInvoke和EndInvoke方法,这样delegate代码是在另外一个线程中执行,这样我们的代码就是一个多线程执行的过程。

搬用书上的一个例子:

我们在程序中创建一个delegate BinaryOp,然后在Main中间调用它。在Main和delegate中分别打印该执行代码所在线程ID:

 

代码
 1 using System.Threading;
 2 using System;
 3 
 4 namespace SyncDelegate
 5 {
 6   public delegate int BinaryOp(int x, int y);
 7 
 8   class Program
 9   {
10     static void Main(string[] args)
11     {
12       Console.WriteLine("***** Synch Delegate Review *****");
13 
14       // Print out the ID of the executing thread.
15       Console.WriteLine("Main() invoked on thread {0}.",
16         Thread.CurrentThread.ManagedThreadId);
17 
18       // Invoke Add() in a synchronous manner.
19       BinaryOp b = new BinaryOp(Add);
20 
21       // Could also write b.Invoke(10, 10);
22       int answer = b(1010);
23 
24       // These lines will not execute until 
25       // the Add() method has completed.
26       Console.WriteLine("Doing more work in Main()!");
27       Console.WriteLine("10 + 10 is {0}.", answer);
28       Console.ReadLine();
29     }
30 
31     #region Very time consuming addition!
32     static int Add(int x, int y)
33     {
34       // Print out the ID of the executing thread.
35       Console.WriteLine("Add() invoked on thread {0}.",
36         Thread.CurrentThread.ManagedThreadId);
37 
38       // Pause to simulate a lengthy operation.
39       Thread.Sleep(5000);
40       return x + y;
41     }
42     #endregion
43   }
44 }

 

 如果查看输出的结果我们可以看到Main输出的线程Id和delegate中的Id是一样的,而且系统会等大概5秒的样子然后显示计算结果,这说明他们是在同一个线程中执行的。

那么我们用什么法子来启动一个异步的线程来调用这个delegate呢?我们可以仔细看看生成的类中间的BeginInvoke和EndInvoke方法。

public IAsyncResult BeginInvoke(int x, int y, AsyncCallback cb, object state);
public int EndInvoke(IAsyncResult result);

一、异步调用的方式

如果我们将原来同步调用的方式作如下的修改:

 

代码
namespace SyncDelegate
{
    
public delegate int BinaryOp(int x, int y);

    
class Program
    {
        
static void Main(string[] args)
        {
            Console.WriteLine(
"**** Synch Delegate Review ****");
            Console.WriteLine(
"Main() invoked on thread {0}.",
                Thread.CurrentThread.ManagedThreadId);

            
// Invoke add() in a synchronous manner
            BinaryOp b = new BinaryOp(Add);
            IAsyncResult iftAR 
= b.BeginInvoke(1010nullnull);

            
int answer = b.EndInvoke(iftAR);

            Console.WriteLine(
"10 + 10 is {0}.", answer);
            Console.ReadLine();
        }

        
static int Add(int x, int y)
        {
            Console.WriteLine(
"Add() invoked on thread: {0}",
                Thread.CurrentThread.ManagedThreadId);
            Thread.Sleep(
5000);
            
return x + y;
        }
    }
}

 

通过调用BeginInvoke方法,启动异步调用,在返回的结果我们也可以看到,Add方法执行的线程Id是不同的。如果要获取函数调用返回的结果,我们就通过EndInvoke方法以BeginInvoke方法的结果作为参数。

二、IAsyncResult

IAsyncResult是一个定义成如下形式的接口:

代码
 1 [ComVisible(true)]
 2 public interface IAsyncResult
 3 {
 4     // Properties
 5     object AsyncState { get; }
 6     WaitHandle AsyncWaitHandle { get; }
 7     bool CompletedSynchronously { get; }
 8     bool IsCompleted { get; }
 9 }

 

它包含了当前执行状况信息。可以传递给其他函数提供这些执行信息。

三、AsyncCallback

BeginInvoke方法和生成的Invoke方法的差别在于后面多了两个参数,AsyncCallback和object.

如果我们去查MSDN或者用reflector查看源代码就发现AsyncCallback是一个定义成如下形式的delegate:

public delegate void AsyncCallback(IAsyncResult ar);

 

就像其本身名字所定义的那样,它是用于回调的,反馈给调用该方法自己执行状况的。比如说,当我们启动一个异步delegate执行的时候,我们想要知道它执行的怎么样了,什么时候执行完。我们可以定义一个AsyncCallback类型的方法,在异步delegate执行的时候调用该方法来通知调用线程。
而对于object参数,这是用来提供一些自定义的数据的。

看如下示例:

 

代码
 1 namespace AsybcDekegate
 2 {
 3     public delegate int BinaryOp(int x, int y);
 4 
 5     class Program
 6     {
 7         static void Main(string[] args)
 8         {
 9             Console.WriteLine("**** AsyncCallback Delegate Example ****");
10             Console.WriteLine("Main() invoked on thread {0}.",
11                 Thread.CurrentThread.ManagedThreadId);
12             
13             BinaryOp b = new BinaryOp(Add);
14             IAsyncResult iftAR = b.BeginInvoke(1010, AddComplete, "Main() thanks you for adding these numbers.");
15             Console.WriteLine("Doing more things in Main");
16             Console.ReadLine();
17 
18         }
19 
20         static void AddComplete(IAsyncResult itfAR)
21         {
22             Console.WriteLine("AddComplete() invoked on thread {0}.",
23                 Thread.CurrentThread.ManagedThreadId);
24             Console.WriteLine("Your addition is complete.");
25 
26             // get the result
27             AsyncResult ar = (AsyncResult)itfAR;
28             BinaryOp b = (BinaryOp)ar.AsyncDelegate;
29             Console.WriteLine("10 + 10 is {0}.",
30                 b.EndInvoke(itfAR));
31             string msg = (string)itfAR.AsyncState;
32             Console.WriteLine(msg);
33         }
34 
35         static int Add(int x, int y)
36         {
37             Console.WriteLine("Add() invoked on thread {0}",
38                 Thread.CurrentThread.ManagedThreadId);
39             Thread.Sleep(5000);
40             return x + y;
41         }
42     }
43 }

 

在这个示例中我们可以自定义一些数据然后AddComplete方法来访问它,itfAR.AsyncState就包含了自定义数据。

posted @ 2009-12-06 12:07  frankliu1980  阅读(2086)  评论(0)    收藏  举报