前文写了关于C#中的异步编程。后台有无数人在讨论,不少人把异步和多线程混了。html
文章在这儿:一文说通C#中的异步编程web
因此,本文从体系的角度,再写一下这个异步编程。编程
1. 异步编程模型c#
这是C#中早期的异步模型,经过IAsyncResult
接口来实现。微信
实现的代码大致是这个样子:多线程
class MyClass
{
IAsyncResult BeginAction(para ..., AsyncCallback callback, object state);
T EndAction(IAsyncResult async_result);
}
这种方式在一些库里还有保留,像FileSteam
类里的BeginRead
和EndRead
方法组,就是这种方式。并发
编程时,不建议用这种方式。app
2. 基于事件的异步模型异步
这是C#中间一个过渡时期的异步模型,核心是基于一个或多个事件、事件处理委托的派生类型,是一种使用多线程的模式。async
这个模式在类库里,多用在Winform/WPF中的组件的事件处理,你能够随便拿一个Framework 4.5之前的组件去研究,大多数都是这种方式。
这种方式的实现大致是这个样子:
class MyClass
{
void ActionAsync(para ...);
event ActionCompletedEventHandler action_completed;
}
这种方式使用多线程,因此,它具备多线程的所有特色和要求。
从微软的建议来看,Framework 4.5之后,并不推荐使用这种模式。
3. 基于任务的异步模型
这种异步模型从Framework 4.0之后引入,使用单一方法来表示异步的开始和完成。这是目前推荐的异步开发方式。在上个文章中的异步模式,就是这个方式。
这个方式的代码实现是这样的:
class MyClass
{
Task<T> ActionAsync(para ...);
}
咱们所说的异步,包括前文讲的异步,所有是基于这个基于任务的异步模型来讨论。
在这个模型下,前文说过,异步不是多线程。今天再强调一遍,异步不只不是多线程,同时异步也不必定会使用多线程。
为了防止不提供原网址的转载,特在这里加上原文连接:http://www.javashuo.com/article/p-actnntok-na.html
先来看看任务:Task
和Task<T>
,这是异步模型的核心。
这个“任务”,是一种“承诺”,承诺会在稍后完成任务。
它有两个关键字:async
和await
。注意:是await
,不是wait
。这儿再强调一下,Task.Wait
是个同步方法,用在多线程中等待。Task
是Thread
的子集,所以继承了Wait
方法,但这个方法不是给异步用的。
在某些状况下,异步能够采用多线程来实现,这时候,Task.Wait
能够用,但这是以多线程的身份来使用的,用出问题要查线程,而不是异步。
关于异步中Task
和async
、await
配合的部分,能够去看前一个文章。地址在:一文说通C#中的异步编程,这儿再也不说了。
1. 单线程模式
先看代码:
Task<string> GetHtmlAsync()
{
var client = new HttpClient();
var gettask = client.GetStringAsync("https://home.cnblogs.com/u/tiger-wang");
return await gettask;
}
这种模式下,这个异步工做于单线程状态。代码虽然返回一个任务Task<T>
,在这个任务依然在主线程中,并无生成一个新的线程。换句话说,这种方式不额外占用线程池资源,也不须要考虑多线程开发中线程锁定、数据一致性等问题。
由于线程没有切换,因此也不存在上下文切换的问题。
2. 多线程模式
既然Task
派生自Thread
,固然也能够用多线程来实现异步。
看代码:
Task<string> GetHtmlAsync()
{
var gettask = Task.Run(() => {
var client = new HttpClient();
return client.GetStringAsync("https://home.cnblogs.com/u/tiger-wang");
});
return await gettask;
}
对方上一段代码,把调用client.GetStringAsync
的部分放到了Task.Run
里。
这种方式中,异步被放到了主线程之外的新线程中执行,换句话说,这个异步在以多线程的方式执行。
在这种模式下,async
和await
的配合,以及对程序执行次序的控制,跟单线程模式是彻底同样的。可是要注意,前边说了,async
和await
是异步的关键字,它无论多线程的事,也不会为多线程提供任何保护。多线程中的并发锁、数据锁、上下文切换,还须要以多线程的方式另外搞定。Task.Run
的内部代码会占用线程池资源,并在一个可用的线程上与主线程并行运行。
1. 取消
异步针对的是须要消耗长时间运行的工做。在工做过程当中,若是须要,咱们能够取消异步的运行。系统提供了一个类CancellationToken
来处理这个工做。
定义方式:
Task<T> ActionAsync(para ..., CancellationToken cancellationtoken);
调用方式:
CancellationTokenSource source = new CancellationTokenSource();
CancellationToken cancel_token = source.Token;
await ActionAsync(para, cancel_token);
须要取消时:
source.Cancel();
就能够了。
在作API时,异步中加个CancellationToken
,是基本的代码礼节。
2. 进度
长时间运行,若是能给出个进度也不错。
定义方式:
Task<T> ActionAsync(para ..., IProgress<T> progress);
其中,T是须要返回的进度值,能够是各类须要的类型。
固然,咱们须要实现IProgress:
public class Progress<T> : IProgress<T>
{
public Progress();
public Progress(Action<T> handler);
protected virtual void OnReport(T value);
public event EventHandler<T> ProgressChanged;
}
IProgress<T>
经过回调来发送进度值,引起捕获并处理。
全文完。
这篇文章是对前一篇文章的补充和扩展。因此,要两篇一块儿看,才更好。
![]() |
微信公众号:老王Plus 扫描二维码,关注我的公众号,能够第一时间获得最新的我的文章和内容推送 本文版权归做者全部,转载请保留此声明和原文连接 |