C#异步模式

.net中提供了执行异步操作的三种模式

异步模式

异步编程模型 (APM) 模式(也称为 IAsyncResult 模式),这是使用 IAsyncResult 接口提供异步行为的旧模型。 在这种模式下,同步操作需要 BeginEnd 方法(例如,BeginWriteEndWrite以实现异步写入操作)。 现在微软不建议新的开发使用此模式。

基于事件的异步模式(EAP)

基于事件的异步模式 (EAP),是提供异步行为的基于事件的旧模型。 这种模式需要后缀为 Async 的方法,以及一个或多个事件、事件处理程序委托类型和 EventArg 派生类型。 EAP 是在 .NET Framework 2.0 中引入的。 现在微软不建议新开发中使用这种模式。

基于任务的异步模式(TAP)

基于任务的异步模式 (TAP),该模式使用单一方法表示异步操作的开始和完成。 TAP 是在 .NET Framework 4 中引入的。 这是在 .NET 中进行异步编程的推荐方法。

从.NET Framework1.0开始,.net中提供了异步属性,而且内置类当中许多类实现了一种或多种异步模式,例如发出HTTP请求并获取返回结果的同步和异步API则是一个例子。

同步调用

下面这个类为WebClient的同步版本。下面的代码片段中,DownloadString发出一个Http请求并将将响应内容赋值给了一个字符串,将该字符串写在控制台上,该代码在执行过程中会等待DownloadString方法执行完之后再执行下面的代码

private const string url = "http://www.cninnovation.com";
private static void SynchronizedAPI()
        {
            Console.WriteLine(nameof(SynchronizedAPI));
            using (var client = new WebClient())
            {
                string content = client.DownloadString(url);
                Console.WriteLine(content.Substring(0, 100));
            }
            Console.WriteLine();
        }

执行结果如下

方法DownloadString会阻塞调用进程,调用的应用程序会无响应直到返回想对应的结果。

异步模式

一般情况下,在.NET中异步模式定义了BeginXXX和EndXXX方法。BeginXXX方法接受其同步方法的所有输入参数,EndXXX方法使用了同步方法的所有输出参数,并按照同步方法的返回类型来返回结果。BeginXXX方法定义了一个AsyncCallback参数

用于接受异步方法执行完成后调用的委托,BeginXXX方法返回IAsyncResult,用于验证调用是否完成,并且一直等到方法的执行结束。

 对于WebClient类并没有对应的异步模式的实现,此处示例使用的是WebRequest类来代替,该类通过BeginGetRespond和EndGetRespond方法提供异步模式。在下面的代码片段中BeginGetResponse将异步HTTP GET请求发到服务器上,一旦网络请求完成则调用本地ReadResponse方法。

private static void AsynchronousPattern()
        {
            Console.WriteLine(nameof(AsynchronousPattern));
            WebRequest request = WebRequest.Create(url);
            IAsyncResult result = request.BeginGetResponse(ReadResponse, null);

            void ReadResponse(IAsyncResult ar)
            {
                using (WebResponse response = request.EndGetResponse(ar))
                {
                    Stream stream = response.GetResponseStream();
                    var reader = new StreamReader(stream);
                    string content = reader.ReadToEnd();
                    Console.WriteLine(content.Substring(0, 100));
                    Console.WriteLine();
                }
            }
        }

 基于事件的异步模式

对于同步方法DownloadString,WebClient类提供了一个异步变体方法DownloadStringAsync。以下代码演示基于事件的异步模式,当请求完成时会触发DownloadStringCompleted事件,该事件的第二个参数是DownloadStringCompleteEventArgs类型,这个类型通过Result

属性返回结果字符串。使用DownloadStringCompleted事件,事件处理程序将通过保存同步上下文的线程来调用,在windform等程序中这就是UI线程。

private static void EventBasedAsyncPattern()
        {
            Console.WriteLine(nameof(EventBasedAsyncPattern));
            using (var client = new WebClient())
            {
                client.DownloadStringCompleted += (sender, e) =>
                {
                    Console.WriteLine(e.Result.Substring(0, 100));
                };
                client.DownloadStringAsync(new Uri(url));
                Console.WriteLine();
            }
        }

基于事件的异步模式和同步编程之间的区别在于方法的调用的顺序,与同步方法的调用相比,顺序被颠倒了,调用异步方法之前需要定义这个方法完成时会发生什么。

基于任务的异步模式

在.NET Framework4.5及以后,对于同步方法DownloadString,Webclient类提供了一个异步变体方法DownloadStringTaskAsync,该方法声明为返回Task<string>,但是不需要声明Task<string>变量,只要声明string类型变量并使用await关键字。

await关键字会解除线程(这里是UI线程)的阻塞,完成其他任务,当DownloadStringTaskAsync方法完成后其后台处理后,UI线程则可以继续,从任务中获得结果,然后执行await关键字之后的代码。

private static async Task Main()
        {
            Console.WriteLine("任务开始");
            TaskBasedAsyncPatternAsync();   //编译器会弹出建议”由于此调用不会等待,因此此调用完成之前将会继续执行当前。请考虑将await运算符用于调用结果“
            Console.WriteLine("任务结束");
            Console.ReadLine();
        }
 
        private static async Task TaskBasedAsyncPatternAsync()
        {
            Console.WriteLine(nameof(TaskBasedAsyncPatternAsync));
            using (var client = new WebClient())
            {
                string content = await client.DownloadStringTaskAsync(url);
                await Task.Delay(3000);
                Console.WriteLine(content.Substring(0, 100));
                Console.WriteLine("这是基于任务的异步编程");
            }
        }

执行结果如下

基于任务的异步模式,编译器上会通过Task任务的形式去实现异步模式,await和async关键字是编译器功能,编译器内部仍然通过Task来实现对应的功能。从以上的执行结果我们可以看到

1.在async方法中遇到await关键字后,当前线程立即返回(到调用方),继续之前的处理逻辑;await关键字之后的代码逻辑,将交由新的线程处理;当新的线程处理完成后,可以从新的线程返回处理结果到调用(处)线程当中,结束等待。

2.在一个async方法中,会根据await关键字进行分割,拆分到不同的线程处理同一个方法的不同部分!

3.把一个方法代码的不同部分拆分到多个线程处理,这是异步方法和同步方法的最大不同!

posted @ 2022-03-20 14:46  说不出来  阅读(285)  评论(0编辑  收藏  举报