背景

自.NET 4.5发布以来已经有很长一段时间了。留在了我们的记忆里,其发布在2012年8月15日。是的,六年前。感觉老了吗?好吧,我不打算让你做出改变,而是提醒你一些.NET发布的亮点。此版本带来的主要功能之一是使用async / await方法进行异步编程。基本上,微软的团队通过保持类似于同步代码的逻辑结构,使编译器完成开发人员过去经常做的工作。

你看,在那个时候,Windows Phone仍然是一件事情,为这些平台开发应用程序有一定的局限性。主要原因是Windows Phone与桌面应用程序不同,引入了硬限制,其中任何方法都无法阻塞超过50ms。反过来,这意味着开发人员不再需要阻止UI,这导致了代码中某种异步性的必要性。.NET 4.5展示了对这种必要性的回应。

那么,为什么我要写一些超过五年前发生过的事情呢?好吧,我注意到尽管这是一个古老的话题,仍然有很多工程师都在努力解决这个问题。引用Mike James在iProgrammer中的话:

通常,程序员完全清楚他们正在做的是面向对象的,但只是模糊地意识到他们正在编写异步代码。

这就是为什么我会尝试在一些博客文章中总结这种编程风格中最重要的部分。此外,我们将尝试确定我们应该使用这种编程风格的情况,以及我们应该避免它的情况。所以,让我们潜入它吧!

动机和使用案例

从本质上讲,这种编程风格适用于您需要能够在等待其他事情完成时做某些事情的任何地方。多年来使用这个工具实际上让我们有能力意识到应该在哪里使用它。它非常适合用户体验,通常用于基于事件的系统,例如基于CPU的并行性或使用文件。

任何可能阻止的活动,例如访问Web,都是此方法的良好候选者。如果这些阻止活动中的任何一个在同步过程中结束,则会阻止整个应用程序。另一方面,如果此活动是异步进程的一部分,则应用程序可以继续处理其他任务,直到活动完成。

当然,你不需要滥用这种能力。例如,我们不应异步更新依赖的记录。这通常是一个坏主意,我们的数据将很快失去同步。除此之外,这个概念有时被过度使用。例如,如果我们正在研究一些简单的行动和操作,我们应该考虑更正统的方法。事实上,在这些情况下使用异步概念可能会带来更多的开销而不是收益。

Async/Await和返回类型

异步机制是在我们的代码中使用async / await关键字在.NET中实现的。我们使用async运算符将我们的方法标记为异步。只有具有此运算符方法的方法才能在其中使用 await运算符。  另一方面,await运算符告诉编译器,在等待异步任务完成之前,使用它的异步方法不能继续超过该点(即await标识的语句行)。基本上,它会暂停方法的执行,直到等待完成任务为止。也可以使用其他方法中的await 运算符调用使用async运算符标记的方法。

另一个重要的事情是异步方法必须返回Task类或Task<TResult>类。这是因为在此方法中,await运算符应用于从另一个异步方法返回的  Task。总结一下,异步机制在.NET中实现如下:

  1. 我们使用async运算符来标记我们想要异步的方法。这些方法必须返回Task类或Task<TResult>类。
  2. 当我们调用用async运算符标记的方法时,我们使用await运算符。此运算符将运行异步方法返回的任务。

如何查看代码呢?看看这个示例WebAccess.cs类:

这个类的目标是以非阻塞的方式访问此网站,并获得响应体的长度,为此  AccessRubiksCodeAsync 方法被使用。我们将 Async后缀放在函数名称的末尾,这是异步方法的标准命名约定。AccessRubiksCodeAsync方法正在调用一些同步的操作,就像函数  LogToConsole一样。 重要的是要意识到GetStringAsync方法也是异步的并且它返回Task。 此Tas k稍后由await运算符运行。

注意我们如何获得同步代码的结构,这很棒。即使我们调用await运算符并且在根本上运行了异步任务,我们的代码看起来也非常简洁。这是async / await机制获得大量普及的原因之一。

那么,如果GetStringAsync方法需要很长时间才能应答,会发生什么?以下是工作流如何运行的:

在第一步(标记为1)中,AccessRubiksCodeAsync方法创建HttpClient的实例并调用此类的GetStringAsync方法。目标是以字符串的形式下载网站的内容。如果发生阻塞GetStringAsync 方法的意外事件,例如,该网站下载时间太长,此方法可以控制其调用者(AccessRubiksCodeAsync)。这就是它避免阻塞资源的方式。除此之外,此方法返回Task<int>,AccessRubiksCodeAsync 将其分配给变量getContent。稍后在代码中,此变量与await 运算符结合使用。

现在,由于我们尚未在getContent 上使用await运算符,因此AccessRubiksCodeAsync方法可以继续执行其他操作,而这些操作不依赖getContent任务结果的。因此,LogToConsole 方法可以以同步方式运行(步骤2)。这意味着此方法将采取控制,完成其工作,然后将控制权交还给AccessRubiksCodeAsync方法。

之后,此方法使用await运算符调用getContent(步骤3)。这意味着此时此方法需要GetStringAsync方法的结果。如果此GetStringAsync方法仍未就绪,则AccessRubiksCodeAsync  将暂停其进度并将控制权返回给其调用者。当然,拥有这种流程的好处是我们“给了一些时间”给GetStringAsync方法,同时,我们运行代码的同步部分。下载内容时,结果返回其长度(步骤4)。

单元测试异步方法

单元测试实际上是如何启动async方法的一个很好的例子。在这个例子中,我使用了xUnit,但是async / await 机制有莪支持其他单元测试框架(如NUnit和MSTests)。如果要将xUnit安装到项目中,请在程序包管理器控制台中输入以下命令:

Install-Package xunit
Install-Package xunit.runner.console
Install-Package xunit.runner.visualstudio
Update-Package

好的,这可以让你快速掌握xUnit。现在让我们看一下WebAccess 类的测试类-WebAcces sTests。

现在,让我们分析一下这个测试的重要部分。首先,请注意我们的测试方法如何使用public async Task进行标记,这与标准的public void不同。这是以如下方式完成的:因为在我们的测试方法中,我们实际上是使用await 运算符调用WebAccess类的AccessRubiksCodeAsync方法。我们可以跳过这个方式并在没有await运算符的情况下调用此方法,并保持public void概念,但结果将不是我们预期的。

如果我们这样做会发生什么——在没有await 运算符的情况下调用async方法?那么,在这种情况下,这个异步方法将作为同步方法执行,这意味着它将阻塞线程。同样,这对于UI和Web操作来说是不利的,并且缩放变得不可能。当然,这在某些情况下可以作为优势使用,但重要的是要知道这种机制是如何工作的。

除此之外,测试结构没有重大变化。我们首先在“arrange”阶段创建WebAccess 类的实例  。在“act”阶段,我们使用await运算符来启动AccessRubiksCodeAsync方法并检索结果。最后,在“assert”阶段,我们检查结果的有效性。

结论

异步编程是.NET多年来的一个有点标准的功能。尽管如此,有时我还是觉得经验不足的程序员不太明白这一点。尤其是那些没有其他技术经验的人,这种机制直接用于技术本身,如Node.js. 或者他们熟悉它并尝试在任何情况下使用它。

原文地址:https://rubikscode.net/2018/05/21/asynchronous-programming-in-net-motivation-and-unit-testing/

.NET中的异步编程——动机和单元测试的更多相关文章

  1. .NET中的异步编程——常见的错误和最佳实践

    在这篇文章中,我们将通过使用异步编程的一些最常见的错误来给你们一些参考. 背景 在之前的文章<.NET中的异步编程——动机和单元测试>中,我们开始分析.NET世界中的异步编程.在那篇文章中 ...

  2. .Net中的异步编程总结

    一直以来很想梳理下我在开发过程中使用异步编程的心得和体会,但是由于我是APM异步编程模式的死忠,当TAP模式和TPL模式出现的时候我并未真正的去接纳这两种模式,所以导致我一直没有花太多心思去整理这两部 ...

  3. C#中的异步编程Async 和 Await

    谈到C#中的异步编程,离不开Async和Await关键字 谈到异步编程,首先我们就要明白到底什么是异步编程. 平时我们的编程一般都是同步编程,所谓同步编程的意思,和我们平时说的同时做几件事情完全不同. ...

  4. javaScript中的异步编程模式

    1.事件模型 let button = document.getElementById("my-btn"); button.onclick = function(event) { ...

  5. Netty 中的异步编程 Future 和 Promise

    Netty 中大量 I/O 操作都是异步执行,本篇博文来聊聊 Netty 中的异步编程. Java Future 提供的异步模型 JDK 5 引入了 Future 模式.Future 接口是 Java ...

  6. 一文说通C#中的异步编程

    天天写,不一定就明白. 又及,前两天看了一个关于同步方法中调用异步方法的文章,里面有些概念不太正确,所以整理了这个文章.   一.同步和异步. 先说同步. 同步概念大家都很熟悉.在异步概念出来之前,我 ...

  7. 一文说通C#中的异步编程补遗

    前文写了关于C#中的异步编程.后台有无数人在讨论,很多人把异步和多线程混了. 文章在这儿:一文说通C#中的异步编程 所以,本文从体系的角度,再写一下这个异步编程.   一.C#中的异步编程演变 1. ...

  8. promise 的基本概念 和如何解决js中的异步编程问题 对 promis 的 then all ctch 的分析 和 await async 的理解

    * promise承诺 * 解决js中异步编程的问题 * * 异步-同步 * 阻塞-无阻塞 * * 同步和异步的区别? 异步;同步 指的是被请求者 解析:被请求者(该事情的处理者)在处理完事情的时候的 ...

  9. .NET中的异步编程

    开篇 异步编程是程序设计的重点也是难点,还记得在刚开始接触.net的时候,看的是一本c#的Winform实例教程,上面大部分都是教我们如何使用Winform的控件以及操作数据库的实例,那时候做的基本都 ...

随机推荐

  1. 第一部分day4-三次登录实验、字符编码

    #-----三次登录实验----- memu = { "陕西":{ "西安市":{ "新城区":["大明宫遗址",&qu ...

  2. iview admin动态路由实现

    参考 https://blog.csdn.net/weixin_41538490/article/details/93749942

  3. 重复的DNA序列[哈希表] LeetCode.187

    所有 DNA 由一系列缩写为 A,C,G 和 T 的核苷酸组成,例如:"ACGAATTCCG".在研究 DNA 时,识别 DNA 中的重复序列有时会对研究非常有帮助. 编写一个函数 ...

  4. Android架构师吐槽腾讯王者荣耀的程序员,排位匹配算法怎么搞的,每次都输

    腾讯王者荣耀的开发来来来出来聊聊,真是日了狗了,多次离上王者还差两三颗星的时候队友就开始水的一塌糊涂,对面就牛逼的不行. 又连跪回去了,被对面把屎都打出来了,实在忍不住来吐槽,你们这个排位匹配算法到底 ...

  5. java Atomic compareAndSet部分原理分析

    以AtomicLong的compareAndSet方法举例.先说结论:如果CPU支持,则基于CPU指令(CMPXCHG8)实现:否则使用ObjectLocker锁实现. 分析过程如下: 该方法在jdk ...

  6. contact form 7如何搭配Akismet过滤垃圾邮件

    contact form 7有很多站长在用,但是经常会有一些垃圾邮件进来,如何过滤呢?两个方法:1.表单提交启用验证码功能,很多垃圾邮件是用软件扫相应的端口,然后批量群发,如果用验证码了可以过滤很大一 ...

  7. 宝塔https部署没成功的原因排查

    今天ytkah在迁移一个客户网站的时候出了点问题,网站从旧的服务器(windows)换到新的服务器(阿里云centos 7,已经安装了宝塔面板),网站之前有用comodo的ssl证书,因为快要过期了, ...

  8. 写入Txt文本信息

    public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { L ...

  9. 解决.Net Core 3.0 不支持 Autofac 问题

    Program.cs using System; using System.Collections.Generic; using System.Linq; using System.Threading ...

  10. Linux修复小技巧

    在重启时不能进入系统,出现以下提示符时.此时输入root密码进入紧急模式,将/etc/fstab文件中除 “/”(根)以外的挂载点全部注释,进系统后在修复排错