C#构建多线程应用程序(1) —— 使用委托来自动创建次线程以实现异步方法的调用



  C#编译器处理委托类型时,它先自动产生一个派生自System.MulticasrDelegate的密封类,该类定义了三个公共方法:Invoke()BeginInvoke()EndInvoke()Invoke()用来以同步方式调用委托对象维护的方法,而BeginInvoke()EndInvoke()方法能在第二个执行线程上异步调用当前的方法。尽管.NET基础类库为多线程专门提供了整个命名空间System.Threading,而委托顺带就提供了这一功能,所有委托都能够异步调用成员,这也是.NET平台最大的优点。


1.委托的同步性


委托的Invoke()方法用来调用被代理对象以同步方式维护的方法。因此,调用委托的线程(比如应用程序的主线程)会一直等待,直到委托调用完成。以下示例是在同步模式下调用Add()方法,Main()方法会一直等到Add()方法调用结束后才输出操作结果。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace SyncDelegate
{
    //定义委托类型
    public delegate int BinaryOp(int x, int y);

    class Program
    {
        static int Add(int x, int y)
        {
            //输出正在执行中的线程ID
            Console.WriteLine("Add() is invoked on thread {0}.", Thread.CurrentThread.ManagedThreadId);
            
            for (int i = 0; i < 5; i++)
            {
                Thread.Sleep(1000);
                Console.WriteLine("Add() has been sleeping for {0} seconds.", i+1);
            }

            return x + y;
        }

        static void Main(string[] args)
        {
            Console.WriteLine("* * * * * Synch Delegate Review * * * * * ");

            //输出正在执行中的线程ID
            Console.WriteLine("Main() is invoked on thread {0}.", Thread.CurrentThread.ManagedThreadId);

            BinaryOp b = new BinaryOp(Add);

            //也可以写成b.Invoke(20, 20),但是一般不直接调用Invoke方法
            int answer = b(20, 20);

            Console.WriteLine("Main() now is working!");

            //获得计算结果
            Console.WriteLine("20 + 20 is {0} !", answer);

            Console.ReadKey();
        }
    }
}

C#中,Invoke()方法并不会直接在代码中被调用,而是在使用“正常的”方法调用语法时在幕后被触发的。Thread.CurrentThread静态属性返回当前线程的引用,Thread.CurrentThread.ManagedThreadId属性返回线程的ID。正如运行结果所示,由于程序中所有的任务都被主线程执行,控制台中显示的是相同的ID值。结果如下:


2.委托的异步性


C#编译器处理delegate关键字的时候,其动态生成的类定义了两个方法BeginInvoke()EndInvoke()。以定义的BinaryOp委托为例,其原型为:

public System.IAsyncResult BeginInvoke(int x, int y, System.AsyncCallback callback, object object);
public int EndInvoke(System.IAsyncResult result);


传入BeginInvoke()的参数,前几个参数必须符合定义的委托类型参数,最后两个参数必须是System.AsyncCallbackSystem.Object类型,同时返回类型为System.IAsyncResultEndInvoke()的返回类型与定义的委托返回类型一致,而参数是System.IAsyncResult类型。

以下示例是以BianryOp委托异步调用Add()方法。

<span style="font-size:12px;">using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace AsyncDelegate
{
    class Program
    {
        public delegate int BinaryOp(int x, int y);

        static int Add(int x, int y)
        {
            //输出正在执行中的线程ID
            Console.WriteLine("Add() is invoked on thread {0}.", Thread.CurrentThread.ManagedThreadId);
</span>
<span style="font-size:12px;">            for (int i = 0; i < 5; i++)
            {
                Thread.Sleep(1000);
                Console.WriteLine("Add() has been sleeping for {0} seconds.", i + 1);
            }

            return x + y;
        }

        static void Main(string[] args)
        {
            Console.WriteLine("* * * * * Async Delegate Invocation * * * * * ");

            //输出正在执行中的线程ID
            Console.WriteLine("Main() is invoked on thread {0}.", Thread.CurrentThread.ManagedThreadId);

            //在新线程中调用Add()
            BinaryOp b = new BinaryOp(Add);
            IAsyncResult iAR = b.BeginInvoke(10, 15, null, null);

            Console.WriteLine("Main() now is working!");

            //获得Add()方法的调用结果
            int answer = b.EndInvoke(iAR);
            Console.WriteLine("10 + 15 is {0}!", answer);

            Console.ReadKey();
</span>        }
    }
}

运行结果如下:


2.1 System.IAsyncResult接口


BeginInvoke()返回的对象实现了IAsyncResult接口,而EndInvoke()需要一个IAsyncResult兼容类型作为它唯一地参数,这就允许线程在调用过BeginInvoke()方法之后,通过EndInvoke()方法获取调用结果。如果定义的委托没有返回值,则仅仅调用BeginInvoke()方法即可。

IAsyncResult接口提供了IsCompleted属性,使用这个属性,在调用EndInvoke()之前,便能判断异步调用是否真正完成。


<span style="font-size:12px;">namespace AsyncDelegateIsCompleted
{
    class Program
    {
        public delegate int BinaryOp(int x, int y);

        static int Add(int x, int y)
        {
            //输出正在执行中的线程ID
            Console.WriteLine("Add() is invoked on thread {0}.", Thread.CurrentThread.ManagedThreadId);

            for (int i = 0; i < 5; i++)
            {
                Thread.Sleep(1000);
                Console.WriteLine("Add() has been sleeping for {0} seconds.", i + 1);
            }

            return x + y;
        }

        static void AddCompleted(IAsyncResult itfAR)
        {
            Console.WriteLine("Add() is completed!");
        }

        static void Main(string[] args)
        {
            Console.WriteLine("* * * * * Async Delegate Invocation * * * * * ");

            //输出正在执行中的线程ID
            Console.WriteLine("Main() is invoked on thread {0}.", Thread.CurrentThread.ManagedThreadId);

            //在新线程中调用Add()
            BinaryOp b = new BinaryOp(Add);
            IAsyncResult iAR = b.BeginInvoke(10, 15, null, null);

            while (iAR.IsCompleted == false)
            {
                Console.WriteLine("The thread which Add() in  now is working!");
            }

            //获得Add()方法的调用结果
            int answer = b.EndInvoke(iAR);
            Console.WriteLine("10 + 15 is {0} !", answer);

            Console.ReadKey();
        }
    }
}</span>


运行结果如下:


相对于IsCompleted属性,IAsyncResult接口提供了AsyncWaitHandle属性来实现更加灵活的等待逻辑。这个属性返回了一个WaitHandle类型的实例,该实例又公开了一个WaitOne()方法。使用WaitHandle. WaitOne()的方法的好处是可以指定最长的等待时间,如果超时返回false


<span style="font-size:12px;">namespace AsyncDelegateIsCompleted
{
    class Program
    {
        public delegate int BinaryOp(int x, int y);

        static int Add(int x, int y)
        {
            //输出正在执行中的线程ID
            Console.WriteLine("Add() is invoked on thread {0}.", Thread.CurrentThread.ManagedThreadId);

            for (int i = 0; i < 5; i++)
            {
                Thread.Sleep(1000);
                Console.WriteLine("Add() has been sleeping for {0} seconds.", i + 1);
            }

            return x + y;
        }

        static void AddCompleted(IAsyncResult itfAR)
        {
            Console.WriteLine("Add() is completed!");
        }

        static void Main(string[] args)
        {
            Console.WriteLine("* * * * * Async Delegate Invocation * * * * * ");

            //输出正在执行中的线程ID
            Console.WriteLine("Main() is invoked on thread {0}.", Thread.CurrentThread.ManagedThreadId);

            //在新线程中调用Add()
            BinaryOp b = new BinaryOp(Add);
            IAsyncResult iAR = b.BeginInvoke(10, 15, null, null);

            while (iAR.AsyncWaitHandle.WaitOne(1000, true) == false)
            {
                Console.WriteLine("The thread which Add() in  now is working!");
            }

            //获得Add()方法的调用结果
            int answer = b.EndInvoke(iAR);
            Console.WriteLine("10 + 15 is {0} !", answer);

            Console.ReadKey();
        }
    }
}</span>


运行结果如下:


2.2 AsyncCallback委托的作用


不通过轮询一个委托来确定异步调用方法是否执行结束,而是在任务完成时由次线程主动通知调用线程的方式会更好。如果想要实现这种方法,需要在调用BeginInvoke()时提供一个System.AsyncCallback委托类型的实例来作为参数,这个参数默认值为null。当异步调用完成的时候,委托便会自动调用该参数指定的方法。


public System.IAsyncResult BeginInvoke(int x, int y, System.AsyncCallback callback, object object);<span lang="EN-US" style="color: black;"><span style="font-family:Times New Roman;">
</span></span>


System.AsyncCallback委托类型的原型为:publicdelegate void AsyncCallback(IAsyncResult ar);

IAsyncResult输入参数被传入到AsyncCallback委托方法中。

如果想好去对分配在Main()中的BinaryOp委托对象的引用,只需把由AsyncDelegate属性返回的System.Object类型转换成BinaryOp类型就可以了。


2.3 BeginInvoke()方法的最后一个参数


异步委托的最后一个需要关注的地方就是BeginInvoke()方法的最后一个参数(默认值为null)该参数允许从主线程传递额外的状态信息给回调方法。因为这个参数类型是System.Object,所以可以传入任何回调方法所希望的类型的数据。而在回调方法中,使用传入IAsyncResult参数的AsyncState属性就可以获得BeginInvoke()方法最后一个参数所传递的信息。


<span style="font-size:12px;">using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Runtime.Remoting.Messaging;

namespace AsyncCallback
{
    class Program
    {
        public delegate int BinaryOp(int x, int y);

        static int Add(int x, int y)
        {
            //输出正在执行中的线程ID
            Console.WriteLine("Add() is invoked on thread {0}.", Thread.CurrentThread.ManagedThreadId);

            for (int i = 0; i < 5; i++)
            {
                Thread.Sleep(1000);
                Console.WriteLine("Add() has been sleeping for {0} seconds.", i + 1);
            }

            return x + y;
        }

        static void AddCompleted(IAsyncResult itfAR)
        {
            Console.WriteLine("AddCompleted() is invoked on thread {0}.", Thread.CurrentThread.ManagedThreadId);

            AsyncResult ar = (AsyncResult)itfAR;
            BinaryOp b = (BinaryOp)ar.AsyncDelegate;
            Console.WriteLine("10 + 15 is {0}!", b.EndInvoke(itfAR));

            //需要类型强制转换
            Console.WriteLine((string)(itfAR.AsyncState));

            Console.WriteLine("Add() is completed!");
        }

        static void Main(string[] args)
        {
            Console.WriteLine("* * * * * Async Delegate Invocation * * * * * ");

            //输出正在执行中的线程ID
            Console.WriteLine("Main() is invoked on thread {0}.", Thread.CurrentThread.ManagedThreadId);

            //在新线程中调用Add()
            BinaryOp b = new BinaryOp(Add);
            IAsyncResult iAR = b.BeginInvoke(10, 15, new System.AsyncCallback(AddCompleted), "Main thanks AddCompleted!");

            Console.ReadKey();
        }

    }
}</span>


运行结果为:


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值