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 对象必须满足以下条件:
    • 实现 INotifyCompletionICriticalNotifyCompletion 接口。
    • 有一个 IsCompleted 属性,返回布尔值。
    • 有一个 GetResult() 方法,返回 void 或一个结果。

这种模式与 可迭代-迭代器模式IEnumerable / IEnumerator)非常相似。

3. 缺失的 IAwaitable / IAwaiter 接口

尽管 .NET 核心没有定义 IAwaitableIAwaiter 接口,但可以通过以下方式模拟它们:

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.dllIObservable<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()
  • 内置支持TaskTaskAwaiter 是最常用的 awaitable / awaiter。
  • Rx 支持:通过 Rx,IObservable<T> 也可以成为 awaitable
kumakoko avatar
kumakoko
pure coder
comments powered by Disqus