C#50异步编程巅峰Word文档格式.docx

上传人:b****5 文档编号:15685980 上传时间:2022-11-15 格式:DOCX 页数:48 大小:41.02KB
下载 相关 举报
C#50异步编程巅峰Word文档格式.docx_第1页
第1页 / 共48页
C#50异步编程巅峰Word文档格式.docx_第2页
第2页 / 共48页
C#50异步编程巅峰Word文档格式.docx_第3页
第3页 / 共48页
C#50异步编程巅峰Word文档格式.docx_第4页
第4页 / 共48页
C#50异步编程巅峰Word文档格式.docx_第5页
第5页 / 共48页
点击查看更多>>
下载资源
资源描述

C#50异步编程巅峰Word文档格式.docx

《C#50异步编程巅峰Word文档格式.docx》由会员分享,可在线阅读,更多相关《C#50异步编程巅峰Word文档格式.docx(48页珍藏版)》请在冰豆网上搜索。

C#50异步编程巅峰Word文档格式.docx

BeginXXX方法和EndXXX方法之间的信息通过一个IAsyncResult对象来传递。

这个对象是BeginXXX方法的返回值。

如果直接调用EndXXX方法,则将以阻塞的方式去等待异步操作完成。

另一种更好的方法是在BeginXXX倒数第二个参数指定的回调函数中调用EndXXX方法,这个回调函数将在异步操作完成时被触发,回调函数的第二个参数即EndXXX方法所需要的IAsyncResult对象。

.NET中一个典型的例子如System.Net命名空间中的HttpWebRequest类里的BeginGetResponse和EndGetResponse这对方法:

IAsyncResultBeginGetResponse(AsyncCallbackcallback,objectstate)

WebResponseEndGetResponse(IAsyncResultasyncResult)

由方法声明即可看出,它们符合前述的模式。

APM使用简单明了,虽然代码量稍多,但也在合理范围之内。

APM两个最大的缺点是不支持进度报告以及不能方便的“取消”。

EAP

在C#.NET第二个版本中,增加了一种新的异步编程模型EAP(Event-basedAsynchronousPattern),EAP模式的异步代码中,典型特征是一个Async结尾的方法和Completed结尾的事件。

XXXCompleted事件将在异步处理完成时被触发,在事件的处理函数中可以操作异步方法的结果。

往往在EAP代码中还会存在名为CancelAsync的方法用来取消异步操作,以及一个ProgressChenged结尾的事件用来汇报操作进度。

通过这种方式支持取消和进度汇报也是EAP比APM更有优势的地方。

通过后文TAP的介绍,你会发现EAP中取消机制没有可延续性,并且不是很通用。

.NET2.0中新增的BackgroundWorker可以看作EAP模式的一个例子。

另一个使用EAP的例子是被HttpClient所取代的WebClient类(新代码应该使用HttpClient而不是WebClient)。

WebClient类中通过DownloadStringAsync方法开启一个异步任务,并有DownloadStringCompleted事件供设置回调函数,还能通过CancelAsync方法取消异步任务。

TAP&

async/await

从.NET4.0开始新增了一个名为TPL的库主要负责异步和并行操作的处理,目标就是使异步和并发操作有个统一的操作界面。

TPL库的核心是Task类,有了Task几乎不用像之前版本的异步和并发那样去和Thread等底层类打交道,作为使用者的我们只需要处理好Task,Task背后有一个名为的TaskScheduler的类来处理Task在Thread上的执行。

可以这样说TaskScheduler和Task就是.NET4.0中异步和并发操作的基础,也是我们写代码时不二的选择。

对于Task可以将其理解为一个包装委托对象(通常就是Action或Func对象)并执行的容器,从Task对象的创建就可以看出:

Actionaction=()=>

Console.WriteLine("

HelloWorld"

);

Tasktask1=newTask(action);

Func<

object,string>

func=name=>

"

+name;

Task<

string>

task2=newTask<

(func,"

hystar"

CancellationToken.None,TaskCreationOptions.None);

//接收object参数真蛋疼,很不容易区分重载,把参数都写上吧。

执行这个Task对象需要手动调用Start方法:

task1.Start();

这样task对象将在默认的TaskScheduler调度下去执行,TaskScheduler使用线程池中的线程,至于是新建还是使用已有线程这个对用户是完全透明的。

还也可以通过重载函数的参数传入自定义的TaskScheduler。

关于TaskScheduler的调度,推荐园子里这篇文章,前半部分介绍了一些线程执行机制,很值得一度。

当我们用new创建一个Task对象时,创建的对象是Created状态,调用Start方法后将变为WaitingToRun状态。

至于什么时候开始执行(进入Running状态,由TaskScheduler控制,)。

Task的创建执行还有一种“快捷方式”,即Run方法:

Task.Run(()=>

));

vartxt=awaitTask<

.Run(()=>

这种方式创建的Task会直接进入WaitingToRun状态。

Task的其他状态还有RanToCompletion,Canceled以及Faulted。

在到大RanToCompletion状态时就可以获得Task<

T>

类型任务的结果。

如果Task在状态为Canceled的情况下结束,会抛出OperationCanceledException。

如果以Faulted状态结束,会抛出导致任务失败的异常。

Task同时服务于并发编程和异步编程(在JeffreyRichter的CLRviaC#中分别称这两种模式为计算限制的异步操作和IO限制的异步操作,仔细想想这称呼也很贴切),这里主要讨论下Task和异步编程的相关的机制。

其中最关键的一点就是Task是一个awaitable对象,这是其可以用于异步编程的基础。

除了Task,还有很多类型也是awaitable的,如ConfigureAwait方法返回的ConfiguredTaskAwaitable、WinRT平台中的IAsyncInfo(这个后文有详细说明)等。

要成为一个awaitable类型需要符合哪些条件呢?

其实就一点,其中有一个GetAwaiter()方法,该方法返回一个awaiter。

那什么是awaiter对象呢?

满足如下3点条件即可:

实现INotifyCompletion或ICriticalNotifyCompletion接口

有bool类型的IsCompleted属性

有一个GetResult()来返回结果,或是返回void

awaitable和awaiter的关系正如IEnumerable和IEnumerator的关系一样。

推而广之,下面要介绍的async/await的幕后实现方式和处理yield语法糖的实现方式差不多。

Task类型的GetAwaiter()返回的awaiter是TaskAwaiter类型。

这个TaskAwaiter很简单基本上就是刚刚满足上面介绍的awaiter的基本要求。

类似于EAP,当异步操作执行完毕后,将通过OnCompleted参数设置的回调继续向下执行,并可以由GetResult获取执行结果。

简要了解过Task,再来看一下本节的重点-async异步方法。

async/await模式的异步也出来很久了,相关文章一大片,这里介绍下重点介绍下一些不容易理解和值得重点关注的点。

我相信我曾经碰到的困惑也是很多人的遇到的困惑,写出来和大家共同探讨。

语法糖

对async/await有了解的朋友都知道这两个关键字最终会被编译为.NET中和异步相关的状态机的代码。

这一部分来具体看一下这些代码,了解它们后我们可以更准确的去使用async/await同时也能理解这种模式下异常和取消是怎样完成的。

先来展示下用于分析反编译代码的例子,一个控制台项目的代码,这是能想到的展示异步方法最简单的例子了,而且和实际项目中常用的代码结构也差不太多:

//实体类

publicclassUser

{

publicintId{get;

set;

}

publicstringUserName{get;

}="

;

publicstringEmail{get;

}

classProgram

staticvoidMain(string[]args)

{

varservice=newService(newRepository());

varname=service.GetUserName

(1).Result;

Console.WriteLine(name);

publicclassService

privatereadonlyRepository_repository;

publicService(Repositoryrepository)

_repository=repository;

publicasyncTask<

GetUserName(intid)

varname=await_repository.GetById(id);

returnname;

publicclassRepository

privateDbContext_dbContext;

privateDbSet<

User>

_set;

publicRepository()

_dbContext=newDbContext("

"

_set=_dbContext.Set<

();

GetById(intid)

{

//IO...

varuser=await_set.FindAsync(id);

returnuser.UserName;

注意:

控制台版本的示例代码中在Main函数中使用了task.Result来获取异步结果,需要注意这是一种阻塞模式,在除控制台之外的UI环境不要使用类似Result属性这样会阻塞的方法,它们会导致UI线程死锁。

而对于没有SynchronizationContext的控制台应用确是再合适不过了。

对于没有返回值的Task,可以使用Wait()方法等待其完成。

这里使用ILSpy去查看反编译后的代码,而且注意要将ILSpy选项中的Decompileasyncmetho

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 求职职场 > 简历

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1