文章目录

  • 异步编程
    • 历史
      • 同步调用
      • 异步模式
      • 基于事件的异步模式
      • 基于任务的异步模式
    • 异步编程基础
      • 创建任务
      • 调用异步方法
      • 使用 Awaiter
      • 延续任务
      • 同步上下文
      • 使用多个异步方法
        • 按顺序调用异步方法
        • 使用组合器
      • 使用 ValueTasks

异步编程

历史

.NETFramework 1.0开始 就提供了异步特性,而fi.NETFramework的许多类都实现了一个或者多个异步模式。下面开始执行同步网络调用,然后介绍不同的异步模式:

  • 异步模式
  • 基于事件的异步模式
  • 基于任务的异步模式

同步调用

class Program
{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阻塞调用线程,直到返回结果。从客户端应用程序的用户界面线程中调用这个方法并 不好,因为它会阻塞用户界面。等待对用户来说是不愉快的,因为在这个网络调用中应用程序没有响应。

异步模式

异步模式定义了 BeginXXX方法和EndXXX方法。例如,如果有一个同步方法DownloadString,其异步
版本就是BeginDownloadString和EndDownloadString方法。BeginXXX方法接受其同步方法的所有输入参数, EndXXX方法使用同步方法的所有输出参数,并按照同步方法的返回类型来返回结果。使用异步模式时, BeginXXX方法还定义了一个AsyncCallback参数,用于接受在异步方法执行完成后调用的委托。BeginXXX方 法返回IAsyncResult,用于验证调用是否己经完成,并且一直等到方法的执行结束。
WebClient类没有提供异步模式的实现方式,但是可以用WebRequest类来替代,因为该类通过
BeginGetResponse和EndGetResponse方法提供这种模式(GetResponse是这个API的同步版本)。
在下面的代码片段中,使用WebRequest类的Create方法仓键WebRequest。使用这个请求对象,BeginGetResponse 方法将异步HTTP GET请求发到服务器。调用线程没有被阻塞。该方法的第一个参数是AsyncCallback。这是一 个通过IAsyncResult参数引用void方法的委托。实现代码是使用本地函数ReadResponse完成的。一旦网络请求完 成,就会调用该方法。在实现代码中,再次通过request对象使用GetResponseStream检索结果

class Program
{private const string url = "http://www.cninnovation.com";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();}}}
}

由于在实现代码中使用了本地函数,因此从外部作用域的请求变量可以通过本地函数的闭包功能直接访问。 lambda表达式也具有类似的行为。如果要使用单独的方法,则必须将请求对象传递给此方法。为此需要将请求 对象传递为BeginGetResponse方法的第二个参数。可以使用IAsyncResult的AsyncState属性在被调用的方法中检索这个参数。

基于事件的异步模式

EventBasedAsyncPattem方法使用了基于事件的异步模式。这个模式定义了一个带有“Async”后缀的方法。对于同步方法DownloadString, WebClient类提供了一个异步变体方法
DownloadStringAsync。当请求完成时,会触发DownloadStringCompleted事件。使用此事件的事件处理程序, 可以检索结果。DownloadStringCompleted 事件类型DownloadStringCompletedEventHandlero 第二个参数是 DownloadStringCompletedEventAigs类型。这个参数通过Result属性返回结果字符串

class Program
{private const string url = "http://www.cninnovation.com";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();}}
}

基于任务的异步模式

该模式定义了一个 带有“Async”后缀的方法,并返回一个Thsk类型。由于WebClient类己经提供了一个带Async后缀的方法来 实现基于任务的异步模式,因此新方法名为DownloadStringTaskAsync

class Program
{private const string url = "http://www.cninnovation.com";private static async Task TaskBasedAsyncPatternAsync(){Console.WriteLine(nameof(TaskBasedAsyncPatternAsync));using (var client = new WebClient()){string content = await client.DownloadStringTaskAsync(url);Console.WriteLine(content.Substring(0, 100));Console.WriteLine();}}
}

异步编程基础

创建任务

static string Greeting(string name)
{TraceThreadAndTask($"running {nameof(Greeting)}");Task.Delay(3000).Wait();return $"Hello, {name}";
}

定义方法GreetingAsync,可以使方法异步化。基于任务的异步模式指定,在异步方法名后加上Async后缀, 并返回一个任务。

static Task<string> GreetingAsync(string name)=>
Task.Run<string>(()=>
{TraceThreadAndTask($'*running {nameof (GreetingAsync) }");
return Greeting(name);
});

调用异步方法

可以使用await关键字来调用返回任务的异步方法GreetingAsync

private static async void CallerWithAsync()
{TraceThreadAndTask($"started {nameof(CallerWithAsync)}");string result = await GreetingAsync("Stephanie");Console.WriteLine(result);TraceThreadAndTask($"ended {nameof(CallerWithAsync)}");
}

使用 Awaiter

可以对任何提供GetAwaiter方法并返回awaiter的对象使用async关键字。awaiter用OnCompleted方法实
现INotifyCompletion接口。此方法在任务完成时调用。

private static void CallerWithAwaiter()
{TraceThreadAndTask($"starting {nameof(CallerWithAwaiter)}");TaskAwaiter<string> awaiter = GreetingAsync("Matthias").GetAwaiter();awaiter.OnCompleted(OnCompleteAwaiter);void OnCompleteAwaiter(){Console.WriteLine(awaiter.GetResult());TraceThreadAndTask($"ended {nameof(CallerWithAwaiter)}");}
}

延续任务

还可以使用Task对象的特性来处理任务的延续。GreetingAsync方法返回一个Task对象。该
Task象包含任务创建的信息,并保存到任务完成。Task类的ContinueWith方法定义了任务完成后就调 用的代码。指派给ContinueWith方法的委托接收将己完成的任务作为参数传入,使用Result属性可以访问任务 返回的结果

private static void CallerWithContinuationTask()
{TraceThreadAndTask($"started {nameof(CallerWithContinuationTask)}");var t1 = GreetingAsync("Stephanie");t1.ContinueWith(t =>{string result = t.Result;Console.WriteLine(result);TraceThreadAndTask($"ended {nameof(CallerWithContinuationTask)}");});
}

同步上下文

为了执行某些动作,有些应用程序会绑定到指定的线程上(例如,在WPF或Windows应用程序中,只有
UI线程才能访问UI元素),这将会是一个问题。如果使用async和await关键字,当await完成之后,不需要进行任何特别处理,就能访问UI线程。默认情况 下,生成的代码会把线程转换到拥有同步上下文的线程中。WPF应用程序设置了DispatcherSynchronizationContext 属性,Windows Forms应用程序设置了WindowsFormsSynchronizationContext属性。Windows应用程序使用 WinRTSynchronizationContext。如果调用异步方法的线程分配给了同步上下文,await完成之后将继续执行。
默认情况下,使用了同步上下文。如果不使用相同的同步上下文,则必须调用Task方法ConfigureAwait
(continueOnCapturedContext: felse)。例如,一个Windows应用程序,其await后面的代码没有用到任何的UI元素。 在这种情况下,避免切换到同步上下文会执行得更快。

使用多个异步方法

在一个异步方法中,可以调用一个或多个异步方法。如何编写代码,取决于一个异步方法的结果是否依赖 于另一个异步方法。

按顺序调用异步方法

使用await关键字可以调用每个异步方法。在有些情况下,如果一个异步方法依赖另一个异步方法的结果, await关键字就非常有用。在这里,GreetingAsync异步方法的第二次调用完全独立于其第一次调用的结果。这 样,如果每个异步方法都不使用await,那么整个MultipleAsyncMethods异步方法将更快地返回结果

private static async void MultipleAsyncMethods()
{string s1 = await GreetingAsync("Stephanie");string s2 = await GreetingAsync("Matthias");Console.WriteLine($"Finished both methods.{Environment.NewLine} Result 1: {s1}{Environment.NewLine} Result 2: {s2}");
}

使用组合器

如果异步方法不依赖于其他异步方法,则每个异步方法都不使用await,而是把每个异步方法的返回结果赋
值给Task变量,就会运行得更快。GreetingAsync方法返回Task<string〉。这些方法现在可以并行运行了。组合 器可以帮助实现这一点。一个组合器可以接受多个同一类型的参数,并返回同一类型的值。多个同一类型的参 数被组合成一个参数来传递。Task组合器接受多个Task对象作为参数,并返回一个Task。

private static async void MultipleAsyncMethodsWithCombinators1()
{Task<string> t1 = GreetingAsync("Stephanie");Task<string> t2 = GreetingAsync("Matthias");await Task.WhenAll(t1, t2);Console.WriteLine($"Finished both methods.{Environment.NewLine} Result 1: {t1.Result}{Environment.NewLine} Result 2: {t2.Result}");
}

使用 ValueTasks

C# 7带有更灵活的await关键字;它现在可以等待任何提供GetAwaiter方法的对象。一种可用于等待的新
类型是ValueTasko与Thsk类相反,ValueTask是一个结构。这具有性能优势,因为ValueTask在堆上没有对象。与异步方法调用相比,及Task对象的实际开销是多少?需要异步调用的方法通常比堆上的对象有更多的开销。
大多数时候,堆上Task对象的开销是可以忽略的,但并不总是这样。例如,某方法可以有一个路径,其中数据 是从一个具有异步API的服务中检索出来的。通过这种数据检索,数据就写入到本地缓存中。第二次调用该方 法时,可以以快速的方式检索数据,而不需要创建Task对象。
示例方法GreetingValueTaskAsync正是这样做的。如果该名称已存在于字典中,则结果返回为ValueTask。 如果名称不在字典中,将调用GreetingAsync方法,该方法返回一个Task。

private static async void UseValueTask()
{TraceThreadAndTask($"start {nameof(UseValueTask)}");string result = await GreetingValueTask2Async("Katharina");Console.WriteLine(result);TraceThreadAndTask($"first result {nameof(UseValueTask)}");string result2 = await GreetingValueTask2Async("Katharina");Console.WriteLine(result2);TraceThreadAndTask($"ended {nameof(UseValueTask)}");
}
static ValueTask<string> GreetingValueTask2Async(string name)
{if (names.TryGetValue(name, out string result)){return new ValueTask<string>(result);}else{Task<string> t1 =  GreetingAsync(name);TaskAwaiter<string> awaiter = t1.GetAwaiter();awaiter.OnCompleted(OnCompletion);return new ValueTask<string>(t1);void OnCompletion(){names.Add(name, awaiter.GetResult());}}
}

C#基础--异步编程相关推荐

  1. [NodeJS]Node异步编程基础

    零.前言 为什么要用Node? Node把非阻塞IO作为提高应用性能的方式.而在JS中,天生拥有着异步编程机制: 事件机制.同时JS中不存在多进程.这样当你执行相对较慢需要花费时间长的IO操作时并不会 ...

  2. java socket 异步回调函数,分享nodejs异步编程基础之回调函数用法

    nodejs异步编程基础之回调函数用法分析 本文实例讲述了nodejs异步编程基础之回调函数用法.分享给大家供大家参考,具体如下: Node.js 异步编程的直接体现就是回调. 异步编程依托于回调来实 ...

  3. async And await异步编程活用基础

    async And await异步编程活用基础 原文:async And await异步编程活用基础 好久没写博客了,时隔5个月,奉上一篇精心准备的文章,希望大家能有所收获,对async 和 awai ...

  4. C#异步编程基础入门总结

    给.neter们整理了一份<.NET/C#面试手册>,目前大约4万字左右,初衷也很简单,就是希望在面试的时候能够帮助到大家,减轻大家的负担和节省时间.对于没有跳槽打算的也可以复习一下相关知 ...

  5. 第一章: Vert.x 异步编程的基础知识

    第一章: Vert.x 异步编程的基础知识 翻译: 白石(https://github.com/wjw465150/Vert.x-Core-Manual) 构建反应式系统的第一步是采用异步编程.基于阻 ...

  6. Boost.Asio基础(五) 异步编程初探

    异步编程 本节深入讨论异步编程将遇到的若干问题.建议多次阅读,以便吃透这一节的内容,这一节是对整个boost.asio来说是非常重要的. 为什么须要异步 如前所述,通常同步编程要比异步编程更简单.同步 ...

  7. ajax请求是宏任务还是微任务_ASP.NET Web API基础(04)---异步编程和跨域请求 - 高原秃鹫...

    异步编程 .1 线程回顾 说到异步编程,离不开多线程.在前面的课程中我们学习过多线程.回顾一下我们之前的例子. public static void DoWork() { (1000); (" ...

  8. 一文了解异步编程基础

    什么是异步编程? 异步编程是指并发编程的范式,其中除了单个主应用程序线程之外,工作可以委托给一个或多个并行工作线程.这被称为非阻塞系统,其中整体系统速度不受订单执行的影响,并且多个进程可以同时发生. ...

  9. 57 Node.js异步编程

    技术交流QQ群:1027579432,欢迎你的加入! 欢迎关注我的微信公众号:CurryCoder的程序人生 1.Node.js异步编程 1.1 Node.js中的异步API 如果异步API后面的代码 ...

最新文章

  1. 线程的CloseHandle和WaitForSingleObject
  2. 如何确定电脑主板坏了_光纤收发器容易坏吗?如何判断光纤收发器的故障?
  3. 全球及中国水彩调色板行业销售前景与投资商机研究报告2022版
  4. DataFactory连接MySQL数据库
  5. OO实现ALV TABLE 九:ALV的事件
  6. C专家编程—使用unsigned int可能产生的Bug(1)
  7. wordpress致命错误怎么解决_pppoe错误是什么意思 pppoe错误怎么解决
  8. 美团Android自动化之旅—适配渠道包
  9. MaxCompute2.0 助力众安保险快速成长
  10. 当你不被上司信任和待见,工作无法正常开展
  11. 老程序员如何避免沦落出局?
  12. 旗袍时尚:青花瓷与青花时装
  13. codeforces 56E 多米诺骨牌效应
  14. 尚学堂视频笔记一:java面向对象基础和java基础知识
  15. Matlab二维正态分布可视化
  16. 电脑提醒没有权限在此位置保存文件怎么办?
  17. Rabbitmq 基础
  18. android 白色圆点,Android通知图标是一个白色圆圈
  19. STM32F030软件SPI控制74HC165
  20. 前端初中级面试题及部分答案

热门文章

  1. 分布式 Session 一致性解决方案
  2. CentOS 停止维护,2022年,快来试试这些最佳替代方案!Rocky Linux(每个版本长达十年的支持,强烈推荐)
  3. 如何包装简历上的项目?
  4. 物理学专业英语(写作整理)04 物理中常用的空间表示
  5. html利用a标签实现下载本地的文件
  6. 粗糙集在计算机网络中的应用,粗糙集神经网络计算机网络故障诊断方法研究
  7. 从Copyright到Copyleft,聊聊版权与开源协议
  8. html鼠标滑过导航栏变色,JavaScript实现鼠标点击导航栏变色特效
  9. 分布式一致性算法理论分析
  10. mysql修改字段的默认值