C#从7.2开始引入了“类任务”(task-like)概念,使
async
与await
的使用不再限制于void
、Task
、Task<T>
类型,我们可以实现自己需要的类型使用异步。不过之前一直没了解过其内部流程,最近借工作需要对这一部分内容进行了学习,并做整理记录。如有问题,望指正。核心记录的问题:
AsyncMethodBuilderAttribute
究竟是什么?- 声明
AsyncMethodBuilderAttribute
时所用的类型有什么用?
首先,一切的发端都是先从roslyn的C#中的异步任务类型看起。
在这篇文章里,将AsyncMethodBuilderAttribute
的作用、自定义Task与Awaiter
的声明规范、自定义的TaskMethodBuilder
及运行机制都进行了明确的阐述。下面将根据这份阐述,结合实际运行的例子进行整理。
自定义TaskMethodBuilder的用途
网上学习时看到一句话:自定义一个TaskMethodBuilder
的目的,是为了供编译器将它的异步机制"绑定"到我们的自定义类型。这么说很晦涩也很抽象,所以我们用具体的例子进行说明。
async void Func(){
await Something();
}
MyTask Something(){
return new MyTask();
}
上面这段代码中,MyTask
就是我们的自定义任务类型。如果这样去使用,那么也就和TaskMethodBuilder
没什么关系了,而其运行结果的本质与await new MyTask()
相同,会依次调用MyTask
的GetAwaiter()
,以及对应Awaiter
的IsCompleted
和GetResult()
。
再看另一个例子。
async void Func(){
await Something();
}
async MyTask Something(){
await new MyTask();
}
上面的这段代码,当运行到await Something()
时,会依次调用TaskMethodBuilder
的Create()
、Start()
、SetResult()
方法。而在Start()
中,我们调用了状态机的MoveNext()
(这也是构建TaskMethodBuilder
类需要遵循的规范之一),在这里会就会执行Something()
中的代码。
所以那段很抽象的话,其实也可以表述为:使用async修饰的自定义任务类型,编译器通过我们自定义的TaskMethodBuilder
实现。
AsyncMethodBuilderAttribute的作用
AsyncMethodBuilderAttribute
作为一个属性,它用于形容一个任务类型(这个任务类型可以是类,也可以是结构体),目的是为了将这个任务类型与一个TaskMethodBuilder
进行关联。如果没有这个属性,可以自己定义,将其放在System.Runtime.CompilerServices
命名空间下即可。
INotifyCompletion与ICriticalNotifyCompletion
INotifyCompletion
与ICriticalNotifyCompletion
这两个接口都定义了OnCompleted(action)
方法,用于任务结束时调用。二者区别在于:INotifyCompletion
用于任务同步完成后被调用,ICriticalNotifyCompletion
用于等待任务完成后被调用。
简单理解,这两个接口一个定义了任务立即完成时的行为,一个定义等待后完成时的行为。不过在Builder的处理中,也体现了二者某种程度上区别不大的特点。