Understanding C# async / await 第2章
Table of Contents
Understanding C# async / await 第1章原文地址
Understanding C# async / await 第2章原文地址
Understanding C# async / await 第3章原文地址
理解 C# 的 async / await (2):Awaitable-Awaiter 模式
1. 什么是 Awaitable?
在 C# 中,任何具有 GetAwaiter()
方法的对象都是 awaitable 的。Task
是最常见的 awaitable 类型,但其他类型也可以实现这一模式。例如:
Task<int> task = new Task<int>(() => 0);
int result = await task.ConfigureAwait(false); // 返回 ConfiguredTaskAwaitable<TResult>
ConfiguredTaskAwaitable<TResult>
是一个 awaitable 类型,但它并不是 Task
。
2. Awaitable-Awaiter 模式
通过观察不同的 awaitable / awaiter 类型,可以总结出以下规则:
- Awaitable 对象必须有一个
GetAwaiter()
方法(实例方法或扩展方法)。 - Awaiter 对象必须满足以下条件:
- 实现
INotifyCompletion
或ICriticalNotifyCompletion
接口。 - 有一个
IsCompleted
属性,返回布尔值。 - 有一个
GetResult()
方法,返回void
或一个结果。
- 实现
这种模式与 可迭代-迭代器模式(IEnumerable
/ IEnumerator
)非常相似。
3. 缺失的 IAwaitable / IAwaiter 接口
尽管 .NET 核心没有定义 IAwaitable
和 IAwaiter
接口,但可以通过以下方式模拟它们:
public interface IAwaitable<out TResult>
{
IAwaiter<TResult> GetAwaiter();
}
public interface IAwaiter<out TResult> : INotifyCompletion
{
bool IsCompleted { get; }
TResult GetResult();
}
这些接口仅用于更好地理解 awaitable / awaiter 模式。
4. 实现 Awaitable / Awaiter
以下是实现 awaitable / awaiter 的示例:
-
FuncAwaitable / FuncAwaiter:
internal struct FuncAwaitable<TResult> : IAwaitable<TResult> { private readonly Func<TResult> function; public FuncAwaitable(Func<TResult> function) => this.function = function; public IAwaiter<TResult> GetAwaiter() => new FuncAwaiter<TResult>(this.function); } public struct FuncAwaiter<TResult> : IAwaiter<TResult> { private readonly Task<TResult> task; public FuncAwaiter(Func<TResult> function) { this.task = new Task<TResult>(function); this.task.Start(); } public bool IsCompleted => this.task.IsCompleted; public TResult GetResult() => this.task.Result; public void OnCompleted(Action continuation) => new Task(continuation).Start(); }
使用方式:
int result = await new FuncAwaitable<int>(() => 0);
-
使用扩展方法:
public static class FuncExtensions { public static TaskAwaiter<TResult> GetAwaiter<TResult>(this Func<TResult> function) { Task<TResult> task = new Task<TResult>(function); task.Start(); return task.GetAwaiter(); } }
使用方式:
int result = await new Func<int>(() => 0);
5. 使用内置的 Awaitable / Awaiter
-
Task 和 TaskAwaiter:
public static class ActionExtensions { public static TaskAwaiter GetAwaiter(this Action action) { Task task = new Task(action); task.Start(); return task.GetAwaiter(); } }
使用方式:
await new Action(() => { });
-
Task.Run():
int result = await Task.Run(() => HelperMethods.IO(arg0, arg1)); await Task.Run(HelperMethods.IO);
6. 使用 Rx(Reactive Extensions)
通过 System.Reactive.Linq.dll
,IObservable<T>
和 IConnectableObservable<T>
也可以成为 awaitable:
private static async Task AwaitObservable1()
{
IObservable<int> observable = Observable.Range(0, 3).Do(Console.WriteLine);
await observable;
}
输出:
0
1
2
另一个示例:
private static async Task<string> AwaitObservable2()
{
IObservable<string> observable = new string[]
{
"https://weblogs.asp.net/dixin/understanding-c-sharp-async-await-1-compilation",
"https://weblogs.asp.net/dixin/understanding-c-sharp-async-await-2-awaitable-awaiter-pattern",
"https://weblogs.asp.net/dixin/understanding-c-sharp-async-await-3-runtime-context",
}
.ToObservable<string>()
.SelectMany(async url => await new WebClient().DownloadStringTaskAsync(url))
.Select(StringExtensions.GetTitleFromHtml)
.Do(Console.WriteLine);
return await observable;
}
输出每个页面的标题:
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
总结
- Awaitable-Awaiter 模式:通过
GetAwaiter()
方法和相关接口,任何类型都可以成为 awaitable。 - 实现方式:可以通过实例方法或扩展方法实现
GetAwaiter()
。 - 内置支持:
Task
和TaskAwaiter
是最常用的 awaitable / awaiter。 - Rx 支持:通过 Rx,
IObservable<T>
也可以成为 awaitable