背景

自.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. python的multitask模块安装

    今天参考网络上的例子,编写基于python的网络程序,遇到下面的错误. No module named 'multitask' 但是multitask的模块却始终下载不了,在网上找到的CSDN下载链接 ...

  2. SRT字幕格式

    [时间:2019-03] [状态:Open] [关键词:字幕,SRT,文件格式] 0 引言 视频文件中最简单.最常见的外挂字幕格式是SRT(SubRip Text).本人找了好久也没找到类似的标准文档 ...

  3. 张兴盼-201871010131 《面向对象程序设计(java)》第六、七周学习总结

    张兴盼-201871010131 <面向对象程序设计(java)>第六.七周学习总结 项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh ...

  4. 【AICC】2019训练营笔记

    1.AI 人工的方法在机器上实现智能:机器学习.计算机视觉.规划决策.自然语言处理.认知推理.高效搜索 2.三大学派 符号主义 连接主义:CNN 行为主义 3.两条路线 结构模仿 功能模仿 4.AI芯 ...

  5. 常用dos命令(3)

    网络命令 ping 进行网络连接测试.名称解析 ftp 文件传输 net 网络命令集及用户管理 telnet 远程登陆 ipconfig显示.修改TCP/IP设置 msg 给用户发送消息 arp 显示 ...

  6. python基础之四:list、tuple

    一.列表 list # 列表 ''' 类似str,可以进行切片 ''' li = [', [1, 2, 3], 55, 'we all in ', 'Tom', ''] print(li[0:2]) ...

  7. Hibernate框架学习1

    框架是什么 1.框架是用来提高开发效率的 2.封装了好了一些功能.我们需要使用这些功能时,调用即可.不需要再手动实现. 3.所以框架可以理解成是一个半成品的项目.只要懂得如何驾驭这些功能即可. orm ...

  8. erlang程序设计--顺序编程

    erlang模块的基本结构 基本结构-module(filename).-export([funname/num]). c(filename). 编译erlang模块  .bean 结尾的文件 编译后 ...

  9. LeetCode 825. Friends Of Appropriate Ages

    原题链接在这里:https://leetcode.com/problems/friends-of-appropriate-ages/ 题目: Some people will make friend ...

  10. Ajax运用与分页

    目录 django与ajax的分页处理 ajax + sweetAlert 实现再次确认: 批量数据插入 分页: django与ajax的分页处理 ajax + sweetAlert 实现再次确认: ...