在学异步,有位园友推荐了《async in C#5.0》,没找到中文版,恰巧也想提升下英文,用我拙劣的英文翻译一些重要的部分,纯属娱乐,简单分享,保持学习,谨记谦虚。html
若是你以为这件事儿没意义翻译的又差,尽情的踩吧。若是你以为值得鼓励,感谢留下你的赞,愿爱技术的园友们在从此每一次应该猛烈突破的时候,不选择知难而退。在每一次应该独立思考的时候,不选择随波逐流,应该尽心尽力的时候,不选择尽力而为,不辜负每一秒存在的意义。web
转载和爬虫请注明原文连接http://www.cnblogs.com/tdws/p/5628538.html,博客园 蜗牛 2016年6月27日。编程
在本章,咱们将会讨论一些关于不使用C#5.0关键字async的异步编程。这种方式虽然已是过去的技术,也许你不会再使用,但这对于你理解异步编程表象背后发生了什么事情是很重要的。也由于这一点,我将会很快的讲述示例,仅仅着重揭示出对你理解有帮助的地方。
正如我以前提到的,Silverlight只提供了像web访问的异步版本API。这里有一个例子,你能够下载一个网页,并显示它:
private void DumpWebPage(Uri uri)
{
WebClient webClient = new WebClient();
webClient.DownloadStringCompleted += OnDownloadStringCompleted;
webClient.DownloadStringAsync(uri);
}
private void OnDownloadStringCompleted(object sender,
DownloadStringCompletedEventArgs eventArgs)
{
m_TextBlock.Text = eventArgs.Result;
}
这种API是基于事件的异步模式(EAP)。这个想法是想替代单线程方法去下载网页,即阻塞型代码会一直等到下载结束再调用一个方法或触发一个事件。这个方法看起来和同步代码同样,除了无返回类型。这个事件也有一个特别的eventArgs类型,它包含值检索。
咱们在调用这个方法前注册了事件。该方法当即返回,固然这是由于它是异步代码。而后在未来的某个时刻触发。这种模式显然很复杂,不只仅是由于你要将它分红像例子同样的两个方法。最重要的是,你注册了一个时间增长了复杂性。若是说我还要用相同的WebClient实例处理其余需求,那么你也许不但愿这个时间依然被附加着而且再次执行一遍。
在.NET功能中另外一个异步模式设计IAsyncResult接口。其中一个例子就是DNS查找主机名的IP地址,BeginGetHoseAddress。这种设计要求两个方法,一个是开始执行的BeginMethodName,另外一个是执行结束EndMethodName,即你的回调方法。
private void LookupHostName()
{
object unrelatedObject = "hello";
Dns.BeginGetHostAddresses("oreilly.com", OnHostNameResolved, unrelatedObject);
}
private void OnHostNameResolved(IAsyncResult ar)
{
object unrelatedObject = ar.AsyncState;
IPAddress[] addresses = Dns.EndGetHostAddresses(ar);
// Do something with addresses
...
}
至少这种方式不会遭受残留注册事件的影响,然而这也额外的对API增长了复杂性。有两个方法而不是一个,我以为很不天然。
这两种异步模式都须要你分为两个方法来书写。IAsyncResult模式要你从第一个方法中向第二个方法传递某些参数,就像我传递了string类型的"hello"。可是这种方式很复杂,即便你不须要这个参数,仍是不得不传递它,而且迫使你转换为object类型。
能够说下面这段代码拥有异步行为,即便不使用async关键字,也不用向方法传递委托:
void GetHostAddress(string hostName, Action<IPAddress> callback)
我发现这种方式比其余方式更加易用。
private void LookupHostName()
{
GetHostAddress("oreilly.com", OnHostNameResolved);
}
private void OnHostNameResolved(IPAddress address)
{
// Do something with address
...
}
不一样于两个方法的模式,像我之前提到的,使用异步方法或者用lambda表达式作回调。它拥有重要的好处就是能够在第一个方法中访问变量。
private void LookupHostName()
{
int aUsefulVariable = 3;
GetHostAddress("oreilly.com", address =>
{
// Do something with address and aUsefulVariable
...
});
}
这个Lambda有一点难以阅读,而且一般若是你使用多重的异步编程,你将须要不少Lambda表达式相互嵌套,你的代码将会很快变得犬牙交错和难以处理。
这种简单方法的缺点在于他们再也不对调用者抛出异常。在以前.NET异步编程中,调用EndMethodName或者获得Result属性时,将会从新抛出异常,因此在代码中咱们能够相应的处理异常。相反,他们可能在某个错误地方中止或者根本不去处理。
任务并行实在.NET Framework4.0版本中推出的。其最重要的地方是Task类,即表明一个正在执行的操做。 泛型版本的Task<T>, 当操做完成时返回类型为T的值。
在C#5.0 async功能上咱们大量的使用了Task,咱们将会稍后讨论。然而即便没有async,你依然可使用Task,尤为是使用Task<T>来异步编程。这样作就行,你开始一个返回Task<T>的操做,而后使用ContinueWith方法注册你的回掉方法。
private void LookupHostName()
{
Task<IPAddress[]> ipAddressesPromise = Dns.GetHostAddressesAsync("oreilly.com");
ipAddressesPromise.ContinueWith(_ =>
{
IPAddress[] ipAddresses = ipAddressesPromise.Result;
// Do something with address
...
});
}
Task的优势就像这个DNS只须要一个方法,使API更加整洁。全部调用异步行为相关的逻辑均可在Task类当中,因此它不须要在每个方法里都进行复制。这个逻辑能够作不少重要的事儿,好比处理异常和同步上下文(SynchronizationContexts)。这些,咱们将会在第八章讨论,对于在一个特定线程上执行callback颇有用处(好比UI线程)。
最重要的是,Task给咱们提供一种使用异步的相对抽象的操做方式。咱们能够利用这种组合型去编写咱们的工具,即在不少须要使用Task的状况下提供给一些有用的行为。咱们将会看到不少相关的工具组件(utilities)在第七章当中。
正如咱们看到的,咱们有不少方式来实现异步编程。有一些方式比其余方式整洁易懂易用,可是也但愿你已经看出他们共有的缺陷。你打算写的程序不得不分为两个方法:实际的方法和回调方法。还有使用异步方法或嵌套屡次lambda表达式做为回调,使你的代码一环套一环难以理解。
实际上这里还有另外一个问题。咱们已经说过调用一次异步方法的状况,可是当你须要多个异步时会发生什么呢?更糟糕的是,若是弄须要在循环中调用异步又会发生什么呢?你为一个方式是使用递归方法,这又比普通的循环难以阅读多了。
private void LookupHostNames(string[] hostNames)
{
LookUpHostNamesHelper(hostNames, 0);
}
private static void LookUpHostNamesHelper(string[] hostNames, int i)
{
Task<IPAddress[]> ipAddressesPromise = Dns.GetHostAddressesAsync(hostNames[i]);
ipAddressesPromise.ContinueWith(_ =>
{
IPAddress[] ipAddresses = ipAddressesPromise.Result;
// Do something with address
...
if (i + 1 < hostNames.Length)
{
LookUpHostNamesHelper(hostNames, i + 1);
}
});
}
哇!
在这些异步编程方式中,引起的另外一个问题就是须要消耗大量代码。若是你写一些异步代码,指望在其余地方使用,你不得不提供API,若是API混乱或者忘记当时的初衷不能理解的话,将会事半功倍。异步代码是会“传染”的,所以不只你须要异步API,还影响调用者和调用者的调用者,知道整个程序乱成一团。
再来谈谈第二章最后一个示例,咱们讨论了一个会因从网站下载icons,形成UI线程阻塞,并致使出现应用程序未响应的WPF UI app。如今咱们将会看到,将它转化成手写的异步代码。
第首先要作的就是找到一个异步API的版本,我用(WebClient。下载文件)。正如咱们已经看到的,WebClient方法使用基于事件的异步方式(EAP),因此咱们能够在开始下载以前注册一个事件做为回调方法。
private void AddAFavicon(string domain)
{
WebClient webClient = new WebClient();
webClient.DownloadDataCompleted += OnWebClientOnDownloadDataCompleted;
webClient.DownloadDataAsync(new Uri("http://" + domain + "/favicon.ico"));
}
private void OnWebClientOnDownloadDataCompleted(object sender,
DownloadDataCompletedEventArgs args)
{
Image imageControl = MakeImageControl(args.Result);
m_WrapPanel.Children.Add(imageControl);
}
固然,咱们的真正属于一块儿的逻辑要被分红两个方法。我不喜欢使用Lambda来代替刚才的EAP,由于lambda会出如今真正开始下载前,我以为这是不可读的。
这个版本的示例也能够在线(https://bitbucket.org/alexdavies74/faviconbrowser)找到,(//译者注释:不运行此程序也不要紧,主要是体会下思路就好)在manual分支。若是你运行它,不进界面可相应,图标也会逐一出现。正所以,咱们也引入了一个bug,如今因为全部下载操做同时开始,icons的排序由其下载前后决定,而不是由个人前后请求来决定。若是你想检验本身是否理解手动编写异步代码,我建议你尝试着解决此bug。在orderedManual分支下(上面列出的站点下)提供了一个解决方案。其余更有效的解决方案也是有可能的。
27号入职,花了三天的业余时间,坎坎坷坷的翻译了第三章。若是你对你有些许益处,不要吝啬你的赞,给个鼓励。不许确和须要补充的地方,也请前辈们不吝赐教,我将虚心改正。下一章将会介绍 “
编写Async方法”