线程实用详解--------(四)异步操作

时间:2021-12-14 23:50:50

在实际的编程中某些任务执行完成时间可能较长,比如打开大文件、连接远程计算机或查询数据库,这个时候如果采用异步操作可以极大提高程序的运行效率,提供良好用户体验。异步操作在主应用程序线程以外的线程中执行。应用程序调用方法异步执行某个操作时,应用程序仍然可以继续执行当前的程序。

下面列举了.NET Framework 中支持异步编程的部分,主要包括: 文件(File) IO、流(Stream) IO、套接字(Socket) IO,网络,远程处理信道(HTTPTCP)和代理,使用 ASP.NET 创建的 XML Web services,ASP.NET Web 窗体,使用 MessageQueue 类的消息队列等。

 谈到异步操作,就不得不说异步委托,可以说异步委托在实现异步操作方面可谓有得天独厚的优势。那么什么是异步委托呢?异步委托就是定义一个方法,开一个新线程,让这个方法在后台执行。委托是方法的类型安全引用。Delegate类支持异步调用的方法,在后台Delegate类会创建一个执行任务的线程。

在.NET平台下,主要通过委托的BeginInvoke()来实现异步操作。

下面我会先通过一个小例子给大家进行解析。

首先创建一个委托

Public delegate void SayHello(string name);

然后在程序的主函数里写下如下代码:

        static void Main(string[] args)

        {

            Console.WriteLine("主线程的ID:" + Thread.CurrentThread.ManagedThreadId);//标记显示主线程ID

            SayHello sayhello = new SayHello(Say);//新建一个委托对象

            sayhello.BeginInvoke("Olive", null, null);//调用委托的异步函数BeginInvoke(),这里边三个参数,第一个参数,为所调用的函数传递的参数,第二、第三个会在下边慢慢介绍

        private static void Say(string name)

        {

            Console.WriteLine("Hello!--------" + name);

            Console.WriteLine("当前的线程ID为:" + Thread.CurrentThread.ManagedThreadId);//显示当前函数执行时所在线程

        }

实验结果如下:

线程实用详解--------(四)异步操作

通过这个简单的小例子我们可以发现:通过委托所实现的异步,不过是又启用了一个新的线程,但是大家要注意的是这个线程是线程池里的线程,是属于后台线程的。

下面我们继续,刚刚我们做的例子是不需要返回结果的异步操作,而实际的开发中,往往需要有返回的结果。那该如何做呢?

首先我们还是要先建一个委托:

 delegate string SayHello(string name);

在主函数中代码如下:

static void Main(string[] args)

        {

            Console.WriteLine("主线程的ID:" + Thread.CurrentThread.ManagedThreadId);//标记显示主线程ID

            SayHello sayhello = new SayHello(Say);

            IAsyncResult iResult=sayhello.BeginInvoke("Olive", null, null);//Delegate.BeginInvoke()方法实际上是一个IAsyncResult的对象

            string result = sayhello.EndInvoke(iResult);//Delegate.EndInvoke()方法就是返回调用函数的返回值,但是该方法需要一个IAsyncResult对象,也就是委托调用BeginInvoke()开始异步

            Console.WriteLine(result);

    Console.ReadKey();

        }

        private static string  Say(string name)

        {

            Console.WriteLine("Hello!--------" + name);

            Console.WriteLine("当前的线程ID为:" + Thread.CurrentThread.ManagedThreadId);

            return "I Love you " + name;

        }

实验结果如下:

线程实用详解--------(四)异步操作

但是这个时候又有一个问题出现了,刚刚我们所举的异步操作只是很简单的返回一句话而已,如果是非常耗时的异步操作的话,我们也不知道操作有没有结束,如果在未结束时调用Delegate.EndInvoke()会一直的处在阻塞状态。这个时候该怎么办呢?

可以有三种办法解决这个问题:

第一个就是反复的询问

只需要在主函数里做如下修改即可:

static void Main(string[] args)

        {

            Console.WriteLine("主线程的ID:" + Thread.CurrentThread.ManagedThreadId);//标记显示主线程ID

            SayHello sayhello = new SayHello(Say);

            IAsyncResult iResult=sayhello.BeginInvoke("Olive", null, null);//Delegate.BeginInvoke()方法实际上是一个IAsyncResult的对象

           If(iResult.IsCompleted)

{           

 string result = sayhello.EndInvoke(iResult);//Delegate.EndInvoke()方法就是返回调用函数的返回值,但是该方法需要一个IAsyncResult对象,也就是委托调用BeginInvoke()开始异步

            Console.WriteLine(result);

} 

   Console.ReadKey();

        }

第二个方法:使用IAsyncReault相关联的等待句柄,使用AsyncWaitHandle属性就可以访问等待句柄。这个属性返回WaitHandle类型的对象,它可以等待委托线程完成其任务。WaitOne()方法将一个超时时间作为可选的第一个参数,在其中可以定义要等待的最长时间。如果发生超时WaitOne方法就返回false

 if (iResult.AsyncWaitHandle.WaitOne(1000,false)) //如果等待超过1秒,则无法返回结果

            {

                string result = sayhello.EndInvoke(iResult);

                Console.WriteLine(result);

            }

第三种方法:异步回调。这个方法是最为推荐的方法。

那么什么是异步回调呢?或许刚刚大家有注意到在调用Delegate.BeginInvoek()方法时,只有有三个参数BeginInvoke(object parameter,AsyncCallback asynccallback,object state),上面只讲第一个参数,也就是委托调用的有参函数所需的参数,多个参数也可以,只需要按照委托声明时的顺序依次添加就可以了,第二个是一个AsyncCallBack类型的委托也就是异步操作执行完后要执行的委托函数,比如需要返回结果、输出什么的都可以在这个委托函数里执行,该委托函数必须有一个IAsyncResult类型的参数。第三个是额外的状态参数,一般都是该委托对象。

下面通过实例给大家进行讲解。

第一步还是先建立一个委托

delegate string  SayHello(string name);

主函数:

        static void Main(string[] args)

        {

   Console.WriteLine("主线程的ID:" + Thread.CurrentThread.ManagedThreadId);//标记显示主  线程ID

            SayHello sayhello = new SayHello(Say);

            IAsyncResult iResult=sayhello.BeginInvoke("Olive",new AsyncCallback(Result), sayhello);//三个参数:1Say()函数的参数,2AsyncCallback类型的委托,(异步操作结束后执行的委托函数),3、将sayhello对象作为状态参数,在Result函数中会有用到该参数

     Console.ReadKey();

        }

         //AsyncCallback委托所执行的函数

        private static void Result(IAsyncResult iasyncresult)

        {

            SayHello sayhello = (SayHello)iasyncresult.AsyncState;//获取IAsyncResult对象的AsyncState的属性,即为Delegate.BeginInvoke()的第三个参数即sayhello

            string s = sayhello.EndInvoke(iasyncresult);

            Console.WriteLine(s + "------好了,异步调用到这里已经结束了");

        }

        private static string  Say(string name)

        {

            Console.WriteLine("Hello!--------" + name);

            Console.WriteLine("当前的线程ID为:" + Thread.CurrentThread.ManagedThreadId);

            return "I Love you " + name;

        }

实验结果如图:

线程实用详解--------(四)异步操作

至此,本节的异步操作已经结束了,在实际的运用中异步操作用的还是比较多的,建议大家在使用异步操作的时候多用下异步回调,这种方法效率比较高、也不会发生异步的阻塞。