p {
display: block;
margin: 3px 0 0 0;
}
-->

写在前面

在学异步,有位园友推荐了《async in C#5.0》,没找到中文版,恰巧也想提高下英文,用我拙劣的英文翻译一些重要的部分,纯属娱乐,简单分享,保持学习,谨记谦虚。

如果你觉得这件事儿没意义翻译的又差,尽情的踩吧。如果你觉得值得鼓励,感谢留下你的赞,愿爱技术的园友们在今后每一次应该猛烈突破的时候,不选择知难而退。在每一次应该独立思考的时候,不选择随波逐流,应该全力以赴的时候,不选择尽力而为,不辜负每一秒存在的意义。

转载和爬虫请注明原文链接http://www.cnblogs.com/tdws/p/5645075.html,博客园 蜗牛 2016年6月27日。

目录
编写Async方法

现在我们已经知道异步代码有多棒了,但是它到底难写吗?是时候来看一看C#5.0的Async功能了。正如我们之前在第三章所看到的,一个async方法允许包含await关键字。

private asyncvoid DumpWebPageAsync(string uri)
{
WebClient webClient = new WebClient();
string page = awaitwebClient.DownloadStringTaskAsync(uri);
Console.WriteLine(page);
}

由于await关键字在此方法中,到await这里就不再继续向下执行,直到下载结束恢复处理。这种处理使此方法异步,在本章,我们将会探索这样的异步方法。

 

将示例代码转换为Async的(第二章最后一个示例)

我们现在将之前那个示例转换成Async的。如果可以,打开原版的代码,在你向下阅读前,尝试将它转换成async和await的方法。

最重要的方法是AddFavicon,即,将下载后的icons添加到UI界面上的方法。我们想把它编程异步的,这样UI线程在下载期间就有空闲去相应用户的操作。第一步要做的就是添加async关键字到方法上。它和static关键字在一样的签名位置。

然后我们需要使用await等待下载。await在C#语法中扮演者医院运算符的角色,就像‘!’或者‘(type)转换操作符’。他被放置在一个表达式的左侧,意于异步的等待表达式。

最后,调用DownloadData方法必须替换成调用异步版本DownloadDataAsync。

Async方法不是自动做到异步的。Async方法仅仅是将调用(消耗consume)其它异步方法更加容易。他们同步地运行着,一直到调用异步方法和await它。当他们做这样的事情时,必须使自身变得异步。有时,一个async方法不await任何事情。

private asyncvoid AddAFavicon(string domain)
{
WebClient webClient = new WebClient();
byte[] bytes = awaitwebClient.DownloadDataTaskAsync("http://" + domain + "/
favicon.ico");
Image imageControl = MakeImageControl(bytes);
m_WrapPanel.Children.Add(imageControl);
}

比较一下这种方式和之前章节所介绍的版本。这看起来更像同步代码的样子。没有任何额外的方法,只在相同结构下有一点额外的代码。然而,他的行为和我们在上一章中的其中一小节(点击跳转)所写的版本很相像。

 

Task和Await

让我们来分解一下我们写的await吧。下面是WebClient.DownloadStringTaskAsync方法。

Task<string> DownloadStringTaskAsync(string address)

它的返回类型是Task<string>。就像我在介绍Task这一小节的介绍,Task代表一个执行中的操作。并且它的子类Task<T>代表着一个在将来某一时刻返回T类型的结果的操作。你可以认为Task<T>承诺返回T类型的值在这个耗时操作之后。

Task和Task<T>都可以代表异步操作,并且都有能力在操作完成后进行回调。在手动实现的异步方式中,你使用ContinueWith方法,传递一个委托,让代码在耗时操作结束后继续下一步操作。await使用相同的方式执行你的剩余的代码(也就是await之后的代码)。

如果你对Task<T>运用await,他成为了一个await expression,并且整个表达式都拥有T类型。这意味着你可以等待一个变量的结果,并且可以在剩余的后半部分方法中使用,就像我们在例子中所看到的。然而当你await一个非泛型Task时,它保持await状态,但不能被分配给任何东西,就像调用一个void方法。这意味着,作为一个Task不承诺返回任何值,他仅仅表示操作本身。

await smtpClient.SendMailAsync(mailMessage);

没有什么可以把我们await表达式内部分开,所以我们可以直接的访问Task,或者在等待中做一些其他事情。具体看下代码和注释你就明白了。

Task<string> myTask = webClient.DownloadStringTaskAsync(uri);
// Do something here
string page = await myTask;

完全理解它带来的启示很重要。DownloadStringTaskAsync方法在第一行执行,他开始在当前线程异步的执行,并且一旦开始了下载,它返回一个Task<string>(对照上面的代码理解),依然在当前线程。只是在后来我们await Task<string>时,编译器做了一些特别的事情。如果你把await写在和调用异步方法在一行代码里它一直是正确的。译者解释:也就是说如果调用await方法,在执行await内部操作的时候,这个线程是当前线程。执行await后的操作,可能是当前线程来处理后面的代码,也可能是新的线程来处理后面的代码。换种方式说,await所等待的方法,被当前线程来执行,但是执行时候立马回收到线程池,下一步操作随机选择一个线程来执行。因此我认为所有认为await会开新线程的说法是错误的。再强调一次,await后之所以会出现新的线程,是因为执行await内部的线程被回收到池子中,从线程池中再取出一个来执行下面的代码。await根本就没有开启线程的功能。

一旦调用DownloadStringTaskAsync发生,耗时操作开始执行,这同时给了我们一个很简单的方法来执行多个异步操作。我们可以开始多个操作,保持Tasks,然后await他们。

Task<string> firstTask = webClient1.DownloadStringTaskAsync("http://oreilly.com");
Task<string> secondTask = webClient2.DownloadStringTaskAsync("http://simple-talk.com");
string firstPage = await firstTask;
string secondPage = await secondTask;

等待多个Task是一种危险的方式,也许他们会抛出异常。如果两个操作抛出一个异常,第一个await将会传播它的异常,这意味着secondTask永远不会被等待。它的异常可能不会被注意到,还取决于.NET版本和设置,也许会丢失或者在另一个非预期线程中抛出,还可能终止该进程。我们将会在第七章讲到更好的方式去处理。

 

Async方法返回类型

标记为async的方法有三种返回类型:

·void

·Task

·Task<T>

没有其他允许的返回类型,因为通常再返回时方法都没执行结束。通常情况下,异步方法将会await一个耗时操作,意思是方法将会迅速返回,但是却在未来实现结果。也意味着,在方法返回时没有明确的结果,而是迟一些才会变得可用。

我会展示方法返回值之间的区别—例如,Task<string>—这个返回类型,即编程人员打算返回给调用者的,在此情况下是string类型。通常,在非异步的方法中,返回类型和结果类型是一样的。但他们之间这样的不同对async方法很重要。

很明显void返回类型是很合理的选择在异步编程情况中。一个async void方法是一个“触发并忘记”的异步操作。调用者不能等待任何返回结果,并且不能知道操作什么时候结束或者是否成功。当你确定你不需要知道操作何时结束或者是否成功时,你应该使用void。async void最常见的应用场景是在async代码和其他代码的边界情况,比如UI事件处理必须返回void。

返回Task的异步方法允许调用者等待操作结束的结果,并且传递在异步代码执行期间的异常。当我们不需要任何返回类值时,一个async Task方法比async void方法更好,因为他允许调用者使用await去等待,并且处理异常更容易。(前面已经说到void最适合的情况)。

最后,返回Task<T>的异步方法,像Task<string>,通常用于异步操作需要返回值的时候。

 

异步,方法签名,和接口

async关键字出现在方法的声明上,就像public和static一样。尽管如此,async不能用于方法的签名,无论是重写方法,实现接口还是被调时。

async关键字唯一的影响是在他所应用的方法内部编译,而不像其他关键字,决定其如何与外界交互。正因如此,在关于重写方法,定义接口的规则上完全不被理会。

class BaseClass
{
public virtual async Task<int> AlexsMethod()
{
...
}
}
class SubClass : BaseClass
{
// This overrides AlexsMethod above
public override Task<int> AlexsMethod()
{
...
}
}

接口不能使用async定义,很简单,因为没必要。如果一个借口需要方法返回Task,在实现时可以使用async,但是用不用还是方法自己的事儿。接口不需要特别声明出是否要异步。

 

Async方法的return Statement

Return Statement在异步方法中有着不同的行为。想想在普通的非异步方法,使用return statement依赖于方法的返回类型。

void方法

return statement只需要return;,并且是可选择。(不写也行)

返回一个T类型的方法

return必须有一个T类型的表达式,比如5+x,并且必须出现在方法所有路径的最后。

在一个标记为async的方法中,不同的情况也有不同的规则

void方法和返回Task的方法

return statement只需要return;,并且是可选择的。(不写也行)

返回Task<T>的方法

return必须返回一个T的表达式并且要在所有返回路径的最后。

在异步方法中,方法的返回类型和表达式类型有所不同。编译器转换可以被认为是将你的结果值包裹起来,在返回给调用者之前。当然,事实上Task>T<立即被创建,并且一旦在你的耗时操作结束后,将你的值“填充”上。译者:像前几章讲的一样,异步方法立即返回被“包裹”的值,在执行结束后,填充值。

 

异步方法具有传染性

正如我们所见,最好的使用由异步返回的Task的方式是在异步方法中await它。当你这样做时,你的方法通常也返回Task。为了享受异步风格的优势,你调用方法的代码必须不是阻塞地等待你的Task结束,并且这样的话,你的调用者很可能也在await你。

下面示例是一个我曾经写过的方法,用于读取一个网页中有多少个字符,并且异步的返回它们。

private async Task<int> GetPageSizeAsync(string url)
{
WebClient webClient = new WebClient();
string page = await webClient.DownloadStringTaskAsync(url);
return page.Length;
}

To use it, I need to write another async method, which returns its result asynchronously为了使用它,我需要写另一个异步的返回自己结果的异步方法,就像这样:

private async Task<string> FindLargestWebPage(string[] urls)
{
string largest = null;
int largestSize = ;
foreach (string url in urls)
{
int size = await GetPageSizeAsync(url);
if (size > largestSize)
{
size = largestSize;
largest = url;
}
}
return largest;
}

在这种方式下,我们不用写异步的方法链,只是每次await就好。Async是一个传染性的编程模型,他可以很容易就弥漫到整个代码体系。但是我认为这正是由于async方法如此容易的书写,这完全没有问题。

 

异步匿名委托和Lambdas

普通的命名方法可以异步,并且有两种匿名方法一样可以异步。语法和正常的方法也很像。下面是如何使用异步匿名委托的示例:

Func<Task<int>> getNumberAsync = async delegate { return ; };

下面是async lambda:

Func<Task<string>> getWordAsync = async () => "hello";

和普通的异步代码规则没什么不一样。你可以用他们来保持代码清晰整洁,捕捉闭合,和非异步方法以完全相同的形式书写。

写在最后

最近好迷茫,可能有点太急躁,总觉得高不成低不就,拼命地想越走越高,又看不到自己明显的进步。痛苦。

下一章节将介绍   await究竟做了什么。

异步编程系列第04章 编写Async方法的更多相关文章

  1. 异步编程系列第01章 Async异步编程简介

    p { display: block; margin: 3px 0 0 0; } --> 2016.10.11补充 三个月过去了,回头来看,我不得不承认这是一系列失败的翻译.过段时间,我将重新翻 ...

  2. 异步编程系列第05章 Await究竟做了什么?

    p { display: block; margin: 3px 0 0 0; } --> 写在前面 在学异步,有位园友推荐了<async in C#5.0>,没找到中文版,恰巧也想提 ...

  3. 异步编程系列第02章 你有什么理由使用Async异步编程

    p { display: block; margin: 3px 0 0 0; } --> 写在前面 在学异步,有位园友推荐了<async in C#5.0>,没找到中文版,恰巧也想提 ...

  4. 【.NET异步编程系列1】:await&async语法糖让异步编程如鱼得水

    前导 Asynchronous programming Model(APM)异步编程模型以BeginMethod(...) 和 EndMethod(...)结对出现. IAsyncResult Beg ...

  5. 异步编程系列06章 以Task为基础的异步模式(TAP)

    p { display: block; margin: 3px 0 0 0; } --> 写在前面 在学异步,有位园友推荐了<async in C#5.0>,没找到中文版,恰巧也想提 ...

  6. JavaScript是如何工作的:事件循环和异步编程的崛起 + 5种使用 async/await 更好地编码方式!

    摘要: 深度理解JS事件循环!!! 原文:JavaScript是如何工作的:事件循环和异步编程的崛起+ 5种使用 async/await 更好地编码方式! 作者:前端小智 Fundebug经授权转载, ...

  7. JS异步编程 (2) - Promise、Generator、async/await

    JS异步编程 (2) - Promise.Generator.async/await 上篇文章我们讲了下JS异步编程的相关知识,比如什么是异步,为什么要使用异步编程以及在浏览器中JS如何实现异步的.最 ...

  8. JavaScript是如何工作的:事件循环和异步编程的崛起+ 5种使用 async/await 更好地编码方式!

    为什么单线程是一个限制? 在发布的第一篇文章中,思考了这样一个问题:当调用堆栈中有函数调用需要花费大量时间来处理时会发生什么? 例如,假设在浏览器中运行一个复杂的图像转换算法. 当调用堆栈有函数要执行 ...

  9. 【.NET异步编程系列2】掌控SynchronizationContext避免deadlock

    引言: 多线程编程/异步编程非常复杂,有很多概念和工具需要去学习,贴心的.NET提供Task线程包装类和await/async异步编程语法糖简化了异步编程方式. 相信很多开发者都看到如下异步编程实践原 ...

随机推荐

  1. ReactJS实践(一)—— FrozenUI React化之Loading组件

    在前面我们通过四篇文章入门了React的大部分主要API,现在则开始进入实践环节. 实践系列的开篇打算拿我司的FrozenUI来试验,将其部分UI组件进行React化,作为第一篇实践文章,将以较简单的 ...

  2. 利用Generator解决异步回调原理

    var i = 0; i++; function ajax(url){ return new Promise(function(resolve, reject){ setTimeout(functio ...

  3. 走向面试之数据库基础:二、SQL进阶之case、子查询、分页、join与视图

    一.CASE的两种用法 1.1 等值判断->相当于switch case (1)具体用法模板: CASE expression WHEN value1 THEN returnvalue1 WHE ...

  4. 备忘-Android ViewPager 子页监听事件

    @Override public Object instantiateItem(View arg0, int arg1) { ((ViewPager) arg0).addView(mListViews ...

  5. 利用Hexo搭建个人博客-环境搭建篇

    我是一个爱写博客进行总结分享的人.然而,有着热爱写博客并且深知写博客好处的我,却没有好好的把这个习惯坚持下来.如今毕业已经一年多了吧,每一次与师弟师妹们聊天,我总会意味深长的建议他们,一定要定期梳理总 ...

  6. Go语言实战 - 创业进行时之创业伊始

    在工作了10年之后,我于32岁的年纪在两个月前辞职创业了. 简单介绍一下之前的整个职业生涯,挺典型的,工程师 –> 资深工程师 –> 架构师 –> 项目经理 –> 部门经理,可 ...

  7. Redis初级介绍

    1 什么是Redis Redis(REmote DIctionary Server,远程数据字典服务器)是开源的内存数据库,常用作缓存或者消息队列. Redis的特点: Redis存在于内存,使用硬盘 ...

  8. C#设计模式-模板方法模式

    提到模板,大家肯定不免想到生活中的“简历模板”.“论文模板”.“Word中模版文件”等,在现实生活中,模板的概念就是——有一个规定的格式,然后每个人都可以根据自己的需求或情况去更新它,例如简历模板,下 ...

  9. 使用Java纯代码实现MySQL的连接

      建立数据库 1. 点击连接-->MySQL: 输入连接名 . 主机名/IP地址 .端口 .用户名.密码(没有密码就省略),然后点击确定,建立的表格是灰色表示关闭状态,双击开启 2. 重新创建 ...

  10. Android播放gif动画,增加屏幕掉金币效果

    前言:播放gif的版本有很多,我这边使用Android自带的Movie类播放gif动画,也是在别人的基础上进行修改.有同样需求的朋友可以参考我的demo. 1.效果图如下: 2.部分主要代码 Main ...