大多数时候,我们写的代码都是同步代码,也就是从上到下按照顺序执行。但有时候遇到耗时较多的任务时,同步方法的弊端往往就会显现出来。例如下载一个较大的文件,如果采用同步代码,那么直到这个文件下载完成之前,我们都无法进行任何操作,这就带来了较差的用户体验。这种情况下就得异步方法出马了。在C#中,Delegate类包含InvokeBeginInvoke两个方法,其中Invoke是同步的,而BeginInvoke则是异步的。先看一段代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    public delegate void DoWorkHandler();

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("开始烧水了");

            // 一直等到水烧开
            DoWorkHandler handler = (() =>
                {
                    Thread.Sleep(5000);
                    Console.WriteLine("水烧好了");
                });
            handler.Invoke();

            // 在水未烧开之前一直会有阻塞
            Console.WriteLine("扫一下地吧");
        }
    }
}

运行结果如下:

C#异步委托_异步


上面的代码描述的是一个烧水的过程,Thread.Sleep(5000)模拟一个耗时操作,例如把水烧开花了5秒钟。这时如果调用Invoke方法,由于Invoke是同步的,线程就会被阻塞,即:在水没有烧开之前我们无法进行其他任何操作。而一般情况下,我们习惯于在烧水的同时还做一些其他的事情,以此提高工作效率和时间利用率,例如在水没有烧开之前,我们可以扫扫地、做做饭之类的。如果要实现一边烧水一边做其他事情,那么就需要用到BeginInvoke方法。看下面一段代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    public delegate void DoWorkHandler();

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("开始烧水了");

            // 水正在烧
            DoWorkHandler handler = (() =>
            {
                Thread.Sleep(5000);
                Console.WriteLine("水烧好了");
            });

            // 这里不会有阻塞,我们可以做一些其他事情
            IAsyncResult asyncResult = handler.BeginInvoke(null, null);
            while (!asyncResult.IsCompleted)
            {
                Console.WriteLine("水还没烧好,先干点其的他事情吧");
                Thread.Sleep(1000);
            }

            // 水烧好了
            handler.EndInvoke(asyncResult);
        }
    }
}

运行结果如下:

C#异步委托_Threading_02

可以看到BeginInvoke并没有阻塞线程,即:在水没有烧好的时间段里,我们可以做一些其他事情。但需要注意,如果EndInvoke没有执行完,线程依旧会阻塞。现在对上面的代码修改一下,改为回调方式,代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    public delegate void DoWorkHandler();

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("开始烧水了");

            // 水正在烧
            DoWorkHandler handler = (() =>
            {
                Thread.Sleep(5000);
                Console.WriteLine("水烧好了");
            });

            // 异步回调
            IAsyncResult asyncResult = handler.BeginInvoke(new AsyncCallback(CallBack), null);
            while (!asyncResult.IsCompleted)
            {
                Console.WriteLine("水还没烧好,先干点其的他事情吧");
                Thread.Sleep(1000);
            }
        }

        static void CallBack(IAsyncResult result)
        {
            DoWorkHandler handler = (result as AsyncResult).AsyncDelegate as DoWorkHandler;
            handler.EndInvoke(result);
        }
    }
}

运行结果如下:

C#异步委托_System_03


上述三种方法更推荐使用回调方法~