Understanding C# async / await 第2章
Understanding C# async / await
Understanding C# async / await 第1章原文地址 Understanding C# async / await 第2章原文地址 Understanding C# async / await 第3章原文地址
2 The Awaitable-Awaiter Pattern
2.1 What is awaitable
第1部分显示了所有Task类都是awaitable的。实际上,还有其他awaitable类型。如下所示:
1Task<int> task = new Task<int>(() => 0);
2int result = await task.ConfigureAwait(false); // Returns a ConfiguredTaskAwaitable<TResult>.
3The returned ConfiguredTaskAwaitable<TResult> struct is awaitable. And it is not Task at all:
4
5public struct ConfiguredTaskAwaitable<TResult>
6{
7 private readonly ConfiguredTaskAwaiter m_configuredTaskAwaiter;
8
9 internal ConfiguredTaskAwaitable(Task<TResult> task, bool continueOnCapturedContext)
10 {
11 this.m_configuredTaskAwaiter = new ConfiguredTaskAwaiter(task, continueOnCapturedContext);
12 }
13
14 public ConfiguredTaskAwaiter GetAwaiter()
15 {
16 return this.m_configuredTaskAwaiter;
17 }
18}
19
ConfiguredTaskAwaitable<TResult> 类有GetAwaiter() 方法,同样地在Task类中也有GetAwaiter() 方法
1public class Task
2{
3 public TaskAwaiter GetAwaiter()
4 {
5 return new TaskAwaiter(this);
6 }
7}
8
9public class Task<TResult> : Task
10{
11 public new TaskAwaiter<TResult> GetAwaiter()
12 {
13 return new TaskAwaiter<TResult>(this);
14 }
15}
Task类的Yield() 方法则是另一个示例:
1await Task.Yield(); // Returns a YieldAwaitable.
2The returned YieldAwaitable is not Task either:
3
4public struct YieldAwaitable
5{
6 public YieldAwaiter GetAwaiter()
7 {
8 return default(YieldAwaiter);
9 }
10}
同样,它也有GetAwaiter() 方法。本文将探讨什么是可等待的。
2.2 The awaitable-awaiter pattern
通过观察不同的awaitable / awaiter类型,我们可以知道一个对象满足以下条件,则是awaitable的
- 它具有GetAwaiter() 方法(可是实例方法或或者扩展方法);
- 它的GetAwaiter() 方法返回一个awaiter。如果满足以下条件,则GetAwaiter() 方法返回的object是一个awaiter:
- 它实现INotifyCompletion或ICriticalNotifyCompletion接口;
- 它有一个IsCompleted属性,该属性必须有一个getter,且该getter返回bool值。
- 它具有GetResult() 方法,该方法返回void或结果。
这种awaitable-awaiter模式和iteratable-iterator模式非常相似。这是iteratable-iterator的接口定义如下:
1public interface IEnumerable
2{
3 IEnumerator GetEnumerator();
4}
5
6public interface IEnumerator
7{
8 object Current { get; }
9 bool MoveNext();
10 void Reset();
11}
12
13public interface IEnumerable<out T> : IEnumerable
14{
15 IEnumerator<T> GetEnumerator();
16}
17
18public interface IEnumerator<out T> : IDisposable, IEnumerator
19{
20 T Current { get; }
21}
2.3 The “missing” IAwaitable / IAwaiter interfaces
与IEnumerable和IEnumerator接口类似,也可以通过IAwaitable / IAwaiter接口可视化awaitable / awaiter对象。下面代码是非泛型版本。
1public interface IAwaitable
2{
3 IAwaiter GetAwaiter();
4}
5
6public interface IAwaiter : INotifyCompletion // or ICriticalNotifyCompletion
7{
8 // INotifyCompletion has one method: void OnCompleted(Action continuation);
9
10 // ICriticalNotifyCompletion implements INotifyCompletion,
11 // also has this method: void UnsafeOnCompleted(Action continuation);
12
13 bool IsCompleted { get; }
14
15 void GetResult();
16}
请注意,非泛型版本的GetResult() 方法在此返回void。Task.GetAwaiter() / TaskAwaiter.GetResult() 方法就是这种情况。下面的代码则是泛型版本的定义
1public interface IAwaitable<out TResult>
2{
3 IAwaiter<TResult> GetAwaiter();
4}
5
6public interface IAwaiter<out TResult> : INotifyCompletion // or ICriticalNotifyCompletion
7{
8 bool IsCompleted { get; }
9
10 TResult GetResult();
11}
和非泛型的相比,泛型版本的GetResult() 方法在此返回结果值。Task<Result>.GetAwaiter() / TaskAwaiter<Result>..GetResult() 方法就是这种情况。
请注意,.NET core没有定义这些IAwaitable / IAwaiter接口。IAwaitable接口将GetAwaiter() 方法约束为实例方法。实际上,C#同时支持GetAwaiter() 实例方法和GetAwaiter() 扩展方法。
在这里,这些接口仅用于更好地让awaitable/awaiter对象可见。现在,如果再次查看上面的ConfiguredTaskAwaitable / ConfiguredTaskAwaiter,YieldAwaitable / YieldAwaiter,Task / TaskAwaiter这些接口,它们都“隐式”实现了这些“缺失的 ” IAwaitable / IAwaiter接口。本文的其余部分将展示如何实现awaitable / awaiter。
2.4 Await any function / action
await不能作用在一个lambda上,如下代码会引发“Cannot await ‘lambda expression’”的编译错误
1int result = await (() => 0);
因为一个lambda可能是一个函数,也可能是一个表达式,存在着歧义,所以会导致编译错误,如果要用lambda,则需要将其赋值到一个Func对象上,如下代码:
1int result = await new Func<int>(() => 0);
这时候消除了歧义了,但又不符合await的使用规范,这时候又会出现一个编译错误“Cannot await ‘System.Func<int>'”。所以必须引入awaitable/awaiter的定义模式来实现她。如果一个对象是awaitable的,则首先要定义一个**GetAwaiter()**方法,如下代码:
1internal struct FuncAwaitable<TResult> : IAwaitable<TResult>
2{
3 private readonly Func<TResult> function;
4
5 public FuncAwaitable(Func<TResult> function)
6 {
7 this.function = function;
8 }
9
10 /* FuncAwaitable<TResult>要成为awaitable的话,其必须返回一个GetAwaiter方法,且该方法返回的对象
11 必须满足以下条件:
12 它实现INotifyCompletion或ICriticalNotifyCompletion接口;
13 它有一个IsCompleted**属性,该属性必须有一个getter,且该getter返回bool值。
14 它具有GetResult()方法,该方法返回void或结果。
15 */
16 public IAwaiter<TResult> GetAwaiter()
17 {
18 return new FuncAwaiter<TResult>(this.function);
19 }
20}
FuncAwaitable<TResult> 类继承实现自IAwaitable<TResult>接口,所以这时候它满足“它具有GetAwaiter()方法(可以是实例方法或者扩展方法)” 这一个条件。这个GetAwaiter() 方法返回一个继承实现自 IAwaiter<TResult> 接口的FuncAwaiter<TResult> 类,该类的定义如下:
1public struct FuncAwaiter<TResult> : IAwaiter<TResult>
2{
3 private readonly Task<TResult> task;
4
5 public FuncAwaiter(Func<TResult> function)
6 {
7 this.task = new Task<TResult>(function);
8 this.task.Start();
9 }
10
11 bool IAwaiter<TResult>.IsCompleted
12 {
13 get
14 {
15 return this.task.IsCompleted;
16 }
17 }
18
19 TResult IAwaiter<TResult>.GetResult()
20 {
21 return this.task.Result;
22 }
23
24 void INotifyCompletion.OnCompleted(Action continuation)
25 {
26 new Task(continuation).Start();
27 }
28}
从上面的代码可以看到FuncAwaiter<TResult> 类的定义实现满足了:
- GetAwaiter() 方法返回的object是一个awaiter:
- 它实现INotifyCompletion或ICriticalNotifyCompletion接口;
- 它有一个IsCompleted属性,该属性必须有一个getter,且该getter返回bool值。
- 它具有GetResult() 方法,该方法返回void或结果。
这三个条件,所以这时候这个FuncAwaitable<TResult> 类是awaiable了,可以用以下方法执行await操作,如下:
1int result = await new FuncAwaitable<int>(() => 0);
2.5 GetAwaiter() extension method, without IAwaitable interfaces
如IAwaitable接口所示,所有需要满足的只是一个GetAwaiter() 方法。在上面的代码中,创建了FuncAwaitable<TResult>作为Func <TResult>的包装,并实现了IAwaitable<TResult>,因此有一个GetAwaiter()实例方法。如果可以为Func <TResult>定义GetAwaiter() 扩展方法,则不再需要FuncAwaitable<TResult> 类,如下代码所示:
1public static class FuncExtensions
2{
3 public static IAwaiter<TResult> GetAwaiter<TResult>(this Func<TResult> function)
4 {
5 return new FuncAwaiter<TResult>(function);
6 }
7}
因此可以直接等待Func <TResult> 类进行await操作:
1int result = await new Func<int>(() => 0);
2.6 Use the built-in awaitable and awaiter: Task and TaskAwaiter
Remember the most frequently used awaitable / awaiter - Task / TaskAwaiter. With Task / TaskAwaiter, FuncAwaitable / FuncAwaiter are no longer needed: 系统提供了内置的基于awaitable/awaiter模式的类,他们是Task / TaskAwaiter类,使用方式如下代码:
1public static class FuncExtensions
2{
3 public static TaskAwaiter<TResult> GetAwaiter<TResult>(this Func<TResult> function)
4 {
5 Task<TResult> task = new Task<TResult>(function);
6 task.Start();
7 return task.GetAwaiter(); // Returns a TaskAwaiter<TResult>.
8 }
9}
类似地如果用扩展方法来定义的话如下:
1public static class ActionExtensions
2{
3 public static TaskAwaiter GetAwaiter(this Action action)
4 {
5 Task task = new Task(action);
6 task.Start();
7 return task.GetAwaiter(); // Returns a TaskAwaiter.
8 }
9}
一個Action類對象可以如下使用awaitan action can be awaited as well:
1await new Action(() => { });
现在所有的方法都可以使用await了
1await new Action(() => HelperMethods.IO()); // or: await new Action(HelperMethods.IO);
如果函数还有参数,可以使用闭包。
1int arg0 = 0;
2int arg1 = 1;
3int result = await new Action(() => HelperMethods.IO(arg0, arg1));
2.7 Use Task.Run()
The above code is used to demonstrate how awaitable / awaiter can be implemented. As it is common scenario to await a function / action, .NET provides a built-in API: Task.Run(). Their implementations are similar to: 上面的代码用于演示如何实现waiting / awaiter。由于等待功能/动作是常见的情况,.NET提供了内置的API:Task.Run()。它们的实现类似于:
1public class Task
2{
3 public static Task Run(Action action)
4 {
5 // The implementation is similar to:
6 Task task = new Task(action);
7 task.Start();
8 return task;
9 }
10
11 public static Task<TResult> Run<TResult>(Func<TResult> function)
12 {
13 // The implementation is similar to:
14 Task<TResult> task = new Task<TResult>(function);
15 task.Start();
16 return task;
17 }
18}
实际上这等待一个Task完成的方法如下:
1int result = await Task.Run(() => HelperMethods.IO(arg0, arg1));
2.8 Await IObservable
如果为Rx(反应性扩展)的一部分System.Reactive.Linq.dll添加了引用,则IObservable<T> 和IConnectableObservable<T> 也将变为等待状态。在此库中,提供了GetAwaiter() 扩展方法:
1public static class Observable
2{
3 public static AsyncSubject<TSource> GetAwaiter<TSource>(this IObservable<TSource> source);
4 public static AsyncSubject<TSource> GetAwaiter<TSource>(this IConnectableObservable<TSource> source);
5}
每个方法都返回一个AsyncSubject<T> 对象,它是一个awaiter:
1public sealed class AsyncSubject<T> : INotifyCompletion, ISubject<T>, ISubject<T, T>, IObserver<T>, IObservable<T>, IDisposable
2{
3 public bool IsCompleted { get; }
4 public void OnCompleted();
5 // ...
6}
这样可以与await关键字一起使用。以IObservable<T> 为例:
1private static async Task AwaitObservable1()
2{
3 IObservable<int> observable = Observable.Range(0, 3).Do(Console.WriteLine);
4 await observable;
5}
输出数值如下:
0 1 2
另一个例子如下:
1private static async Task<string> AwaitObservable2()
2{
3 IObservable<string> observable = new string[]
4 {
5 "https://weblogs.asp.net/dixin/understanding-c-sharp-async-await-1-compilation",
6 "https://weblogs.asp.net/dixin/understanding-c-sharp-async-await-2-awaitable-awaiter-pattern",
7 "https://weblogs.asp.net/dixin/understanding-c-sharp-async-await-3-runtime-context",
8 }
9 .ToObservable<string>()
10 .SelectMany(async url => await new WebClient().DownloadStringTaskAsync(url))
11 .Select(StringExtensions.GetTitleFromHtml)
12 .Do(Console.WriteLine);
13
14 return await observable;
15}
GetTitleFromHtml方法如下:
1public static string GetTitleFromHtml(this string html)
2{
3 Match match = new Regex(
4 @".*<head>.*<title>(.*)</title>.*</head>.*",
5 RegexOptions.IgnoreCase | RegexOptions.Singleline).Match(html);
6 return match.Success ? match.Groups[1].Value : null;
7}
执行上面的AwaitObservable2方法将输出每个页面的标题:
Dixin's Blog - Understanding C# async / await (1) Compilation Dixin's Blog - Understanding C# async / await (3) Runtime Context Dixin's Blog - Understanding C# async / await (2) The Awaitable-Awaiter Pattern
正是<tile>和</title>之间的内容。