C# 创建线程的多种方式之异步调用

大家好,我是阿辉。

创建线程一种简单的方式是委托的异步调用,Delegate类提供了BeginInvoke方法,该方法可以传递委托类型定义的参数(所以BeginInvoke参数数量是可变的),另外还有2个固定的参数 回调函数委托AsynsCallBack和类型Object(如果不使用可直接赋值为null)。

BeginInvoke() 的返回值为IAsynResult,通过它的IsComplete属性可以判断异步调用是否完成。.

static void Main(string[] args)
        {
            Func<int, int> act1 = new Func<int, int>(Calculate);
            Console.WriteLine("Main Start....");
            IAsyncResult ar = act1.BeginInvoke(20,null, null);
            while (!ar.IsCompleted)
            {
                Console.WriteLine("Main is waiting");
                Thread.Sleep(500);
            }
            int result = act1.EndInvoke(ar);
            Console.WriteLine("Result is " + result);
            Console.ReadLine();
        }

        private static int Calculate(int total)
        {
            int sum = 0;
            for (int i = 0; i < total;i++ )
            {
                sum += i;
                Thread.Sleep(100);
            }
            return sum;
        }

运行返回结果:

Main Start....
Main is waiting
Main is waiting
Main is waiting
Main is waiting
Main is waiting
Result is 190

事实上,EndInvoke也是会等到异步调用结束,返回结果的。此外,IAsynResult的AsyncWaitHandle属性是WaitHandle类型,利用WaitOne()能够达到上述同样的效果,我觉得有一个好处就是可以利用WaitAll()等待多个异步调用同时完成:

static void Main(string[] args)
        {
            Func<int, int> act1 = new Func<int, int>(Calculate);
            Func<int, int> act2 = new Func<int, int>(Calculate);
            Console.WriteLine("Main Start....");
            IAsyncResult ar1 = act1.BeginInvoke(20,null, null);
            IAsyncResult ar2 = act2.BeginInvoke(30, null, null);
            if(WaitHandle.WaitAll(new WaitHandle[]{ar1.AsyncWaitHandle,ar2.AsyncWaitHandle},5000))
            {
                Console.WriteLine("Waiting is over");
                
            }
            int result1 = act1.EndInvoke(ar1);
            int result2 = act2.EndInvoke(ar2);
            Console.WriteLine("Result1 is {0},Result2 is {1}", result1, result2);
            Console.ReadLine();
        }

        private static int Calculate(int total)
        {
            int sum = 0;
            for (int i = 0; i < total;i++ )
            {
                sum += i;
                Thread.Sleep(100);
            }
            return sum;
        }

我声明了act1和act2两个委托对象,并同时进行异步调用,WaitHandle.WaitAll(new WaitHandle[]{ar1.AsyncWaitHandle,ar2.AsyncWaitHandle},5000) 等待同时完成,最后返回结果:

Main Start....
Waiting is over
Result1 is 190,Result2 is 435

除了以上2种方式返回运行结果,还可以使用AsynsCallBack回调,BeginInvoke()的最后一个参数可以用ar.Asynstate访问,以便在回调函数中使用,例如传递委托实例获取运行结果:

static void Main(string[] args)
        {
            Func<int, int> act1 = new Func<int, int>(Calculate);
            Console.WriteLine("Main Start....");
            IAsyncResult ar1 = act1.BeginInvoke(20, new AsyncCallback(CalculateComplete), act1);
            while(!ar1.IsCompleted){
                Console.WriteLine("Main is waiting");
                Thread.Sleep(500);
            }
            Console.ReadLine();
        }

        private static int Calculate(int total)
        {
            int sum = 0;
            for (int i = 0; i < total;i++ )
            {
                sum += i;
                Thread.Sleep(100);
            }
            return sum;
        }

        private static void CalculateComplete(IAsyncResult ar)
        {
            Console.WriteLine("Counting is over...........");
            Func<int, int> act = ar.AsyncState as Func<int, int>;
            Console.WriteLine("Result is "+act.EndInvoke(ar));
        }

运行结果:

Main Start....
Main is waiting
Main is waiting
Main is waiting
Main is waiting
Main is waiting
Counting is over...........
Result is 190

回调函数是在委托线程中完成的,还可以使用Lamda表达式,更加简便,传入最后一个参数,因为Lamda表达式可以访问域外变量:

      static void Main(string[] args)
        {
            Func<int, int> act1 = new Func<int, int>(Calculate);
            Console.WriteLine("Main Start....");
            IAsyncResult ar1 = act1.BeginInvoke(20, ar => { Console.WriteLine("Result is " + act1.EndInvoke(ar)); }, null);
            while(!ar1.IsCompleted){
                Console.WriteLine("Main is waiting");
                Thread.Sleep(500);
            }
            Console.ReadLine();
        }