代码改变世界

另一种将线程并入应用程序的方法-委托

2012-04-28 16:18  yezhi  阅读(641)  评论(2编辑  收藏  举报

委托的同步

声明一个委托:

public delegate int BinaryOp(int x, int y);

BinaryOp编译后,其所属程序集将包含一个根据委托声明动态生成的类的定义,将会生成如下三个方法:

   |   |   |___[MET] BeginInvoke : class [mscorlib]System.IAsyncResult(int32,int32,class [mscorlib]System.AsyncCallback,object)
   |   |   |___[MET] EndInvoke : int32(class [mscorlib]System.IAsyncResult)
   |   |   |___[MET] Invoke : int32(int32,int32)

Invoke()方法用来调用被代理对象以同步方式维护的方法。因此,调用委托的线程(比如应用程序的主线程)将会一直等待,直到委托调用完成。

BeginInvoke()用于异步调用方法。

EndInvoke()用于获取被调用方法的返回值。后两个方法在后面异步调用方法中谈到。

看则小例:

  public delegate int BinaryOp(int x, int y);

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("***** 同步方式 *****");

            Console.WriteLine("线程标识{0}.",
              Thread.CurrentThread.ManagedThreadId);

            //同步模式下调用Add()
            BinaryOp b = new BinaryOp(Add);

            //也可写成b.Invoke(10, 10);
            int answer = b(10, 10);

            //直到b(10, 10) Add()方法完成后,这行代码才会执行
            Console.WriteLine("Add()方法完成了!");
            Thread.Sleep(3000);

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

        static int Add(int x, int y)
        {
            Console.WriteLine("线程标识{0}.",
              Thread.CurrentThread.ManagedThreadId);

            //模拟耗时
            Thread.Sleep(5000);
            return x + y;
        }
    }

输出结果:

 异步调用方法

小例:

public delegate int BinaryOp(int x, int y);

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("***** 异步方式 *****");

            Console.WriteLine("线程标识{0}.",
              Thread.CurrentThread.ManagedThreadId);

            //次线程下调用Add()
            BinaryOp b = new BinaryOp(Add);
            IAsyncResult iAR = b.BeginInvoke(10, 10, null, null);

            //不用等待Add()方法完成
            Console.WriteLine("Add()方法正在执行中……,这里可以在主线程做下其它的事情。");

            int answer = b.EndInvoke(iAR);
            Console.WriteLine("Add()已完成,10 + 10 = {0}.", answer);
            Console.WriteLine("以上可以看到,两个不同的线程ID,说明是两个线程在运行");
            Console.ReadLine();
        }

        static int Add(int x, int y)
        {
            Console.WriteLine("线程标识{0}.",
              Thread.CurrentThread.ManagedThreadId);

            //模拟耗时
            Thread.Sleep(5000);
            return x + y;
        }
    }

输出结果:

 同步调用线程,通过IAsyncResult接口提供的Iscompleted属性来判断异步调用是否完成

 小例:

public delegate int BinaryOp(int x, int y);

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("***** 异步方式 *****");

            Console.WriteLine("线程标识{0}.",
              Thread.CurrentThread.ManagedThreadId);

            //次线程下调用Add()
            BinaryOp b = new BinaryOp(Add);
            IAsyncResult iAR = b.BeginInvoke(10, 10, null, null);

            //当次线程还未完成时,在主线程可以做些事情
            //可使用IsCompleted属性判断实现一种同步
            while (!iAR.IsCompleted)
            {
                Console.WriteLine("你完成了吗?这里可以在主线程做下其它的事情。");
                Thread.Sleep(1000);
            }

            int answer = b.EndInvoke(iAR);


            Console.WriteLine("完成啦,10 + 10 = {0}.", answer);
            Console.ReadLine();
        }

        static int Add(int x, int y)
        {
            Console.WriteLine("线程标识{0}.",
              Thread.CurrentThread.ManagedThreadId);

            //模拟耗时
            Thread.Sleep(5000);
            return x + y;
        }
    }

输出结果:

AsyncCallback委托

虽然IAsyncResult的这些属性提供了同步调用线程的方式,不过不是最高效的,也让人蛋疼,不停的问你“完成了吗?”,

不通过轮询一个委托来确定异步调用方法执行是否结束,而是在任务完成时由次线程主动通知调用线程的方式,这样更好。

实现这种方式,需要调用BeginInvoke()时的提供的一个System.AsyncCallback委托的实例作为参数。

当异步调用完成时,委托便会自动调用(AsyncCallback对象)指定的方法。

小例:

public delegate int BinaryOp(int x, int y);

    class Program
    {
        private static bool isDone = false;
        static void Main(string[] args)
        {
            Console.WriteLine("***** 异步方式 *****");

            Console.WriteLine("线程标识{0}.",
              Thread.CurrentThread.ManagedThreadId);

            //次线程下调用Add()
            BinaryOp b = new BinaryOp(Add);
            IAsyncResult iAR = b.BeginInvoke(10, 10, new AsyncCallback(AddComplete), null);
            

            while (!isDone)
            {
                Thread.Sleep(1000);
                Console.WriteLine("次线程工作中……");
            }
            
            Console.ReadLine();
        }

        static int Add(int x, int y)
        {
            Console.WriteLine("线程标识{0}.",
              Thread.CurrentThread.ManagedThreadId);

            //模拟耗时
            Thread.Sleep(5000);
            return x + y;
        }

        static void AddComplete(IAsyncResult iAR)
        {
            Console.WriteLine("线程标识{0}.",
                         Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine("Add()方法已完成了。");

            //因为这里访问不到BinaryOp委托,所有先获取BinaryOp委托对象
            AsyncResult ar = (AsyncResult)iAR;
            BinaryOp b = ar.AsyncDelegate as BinaryOp;
            Console.WriteLine("10 + 10 = {0}.", b.EndInvoke(iAR));
            isDone = true;
        }
    }

输出结果:

传递和接收自定义状态的数据

异步委托最后一个关注的地方是BeginInvoke()方法的最后一个参数,该参数允许主线程传递额外的状态信息给回调方法。

因为这个参数类型是object类型,所有可以传入任何类型的数据。

小例:

public delegate int BinaryOp(int x, int y);

    class Program
    {
        private static bool isDone = false;
        static void Main(string[] args)
        {
            Console.WriteLine("***** 异步方式 *****");

            Console.WriteLine("线程标识{0}.",
              Thread.CurrentThread.ManagedThreadId);

            //次线程下调用Add()
            BinaryOp b = new BinaryOp(Add);
            IAsyncResult iAR = b.BeginInvoke(10, 10, new AsyncCallback(AddComplete), "2 这是从主线程自定义传进来的参数");


            while (!isDone)
            {
                Thread.Sleep(1000);
                Console.WriteLine("次线程工作中……");
            }

            Console.ReadLine();
        }

        static int Add(int x, int y)
        {
            Console.WriteLine("线程标识{0}.",
              Thread.CurrentThread.ManagedThreadId);

            //模拟耗时
            Thread.Sleep(5000);
            return x + y;
        }

        static void AddComplete(IAsyncResult iAR)
        {
            Console.WriteLine("线程标识{0}.",
                         Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine("Add()方法已完成了。");

            //因为这里访问不到BinaryOp委托,所有先获取BinaryOp委托对象
            //AsyncResult ar = (AsyncResult)iAR;
            //BinaryOp b = ar.AsyncDelegate as BinaryOp;

            //iar.AsyncState这里将实现强制转换,所以主线程和次线程必段认同由AsyncState返回的实际类型
            Console.WriteLine("10 + 10 = {0}.", iAR.AsyncState.ToString());
            isDone = true;
        }
    }

输出结果: