Understanding C# async / await 第1章

Table of Contents

Understanding C# async / await 第1章原文地址

Understanding C# async / await 第2章原文地址

Understanding C# async / await 第3章原文地址


以下是基于你提供的内容的总结和解读,保留示例代码和关键细节:


理解 C# 的 async / await (1):编译过程

1. 概述

C# 的 asyncawait 关键字为异步编程提供了极大的便利。本文将深入探讨这些语法糖背后的实际代码实现。

2. 准备工作

首先,定义一些辅助方法:

internal class HelperMethods
{
    private static void IO()
    {
        using (WebClient client = new WebClient())
        {
            Enumerable.Repeat("http://weblogs.asp.net/dixin", 10).Select(client.DownloadString).ToArray();
        }
    }

    internal static int Method(int arg0, int arg1)
    {
        int result = arg0 + arg1;
        IO(); // 模拟长时间运行的 IO 操作
        return result;
    }

    internal static Task<int> MethodTask(int arg0, int arg1)
    {
        Task<int> task = new Task<int>(() => Method(arg0, arg1));
        task.Start(); // 返回已启动的任务
        return task;
    }

    internal static void Before() { }
    internal static void Continuation1(int arg) { }
    internal static void Continuation2(int arg) { }
}

Method() 是一个模拟长时间 IO 操作的方法,MethodTask() 将其包装为 Task 并返回。

3. 使用 async / await

定义一个异步方法 MethodAsync(),并在其中使用 await

internal class AsyncMethods
{
    internal static async Task<int> MethodAsync(int arg0, int arg1)
    {
        int result = await HelperMethods.MethodTask(arg0, arg1);
        return result;
    }
}

await 关键字用于等待 MethodTask() 返回的 Task

4. 编译后的代码

编译后,MethodAsync() 的代码会被转换为以下形式:

internal class CompiledAsyncMethods
{
    [DebuggerStepThrough]
    [AsyncStateMachine(typeof(MethodAsyncStateMachine))] // async
    internal static /*async*/ Task<int> MethodAsync(int arg0, int arg1)
    {
        MethodAsyncStateMachine methodAsyncStateMachine = new MethodAsyncStateMachine()
        {
            Arg0 = arg0,
            Arg1 = arg1,
            Builder = AsyncTaskMethodBuilder<int>.Create(),
            State = -1
        };
        methodAsyncStateMachine.Builder.Start(ref methodAsyncStateMachine);
        return methodAsyncStateMachine.Builder.Task;
    }
}

async 关键字被移除,实际逻辑被转移到一个状态机 MethodAsyncStateMachine 中:

[CompilerGenerated]
[StructLayout(LayoutKind.Auto)]
internal struct MethodAsyncStateMachine : IAsyncStateMachine
{
    public int State;
    public AsyncTaskMethodBuilder<int> Builder;
    public int Arg0;
    public int Arg1;
    public int Result;
    private TaskAwaiter<int> awaitor;

    void IAsyncStateMachine.MoveNext()
    {
        try
        {
            if (this.State != 0)
            {
                this.awaitor = HelperMethods.MethodTask(this.Arg0, this.Arg1).GetAwaiter();
                if (!this.awaitor.IsCompleted)
                {
                    this.State = 0;
                    this.Builder.AwaitUnsafeOnCompleted(ref this.awaitor, ref this);
                    return;
                }
            }
            else
            {
                this.State = -1;
            }

            this.Result = this.awaitor.GetResult();
        }
        catch (Exception exception)
        {
            this.State = -2;
            this.Builder.SetException(exception);
            return;
        }

        this.State = -2;
        this.Builder.SetResult(this.Result);
    }

    [DebuggerHidden]
    void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine param0)
    {
        this.Builder.SetStateMachine(param0);
    }
}

状态机的 MoveNext() 方法包含了实际的异步逻辑。

5. 多 await 方法

定义一个更复杂的异步方法 MultiCallMethodAsync(),包含多个 await 和 continuation 代码:

internal class AsyncMethods
{
    internal static async Task<int> MultiCallMethodAsync(int arg0, int arg1, int arg2, int arg3)
    {
        HelperMethods.Before();
        int resultOfAwait1 = await MethodAsync(arg0, arg1);
        HelperMethods.Continuation1(resultOfAwait1);
        int resultOfAwait2 = await MethodAsync(arg2, arg3);
        HelperMethods.Continuation2(resultOfAwait2);
        int resultToReturn = resultOfAwait1 + resultOfAwait2;
        return resultToReturn;
    }
}

编译后,MultiCallMethodAsync() 也会被转换为状态机 MultiCallMethodAsyncStateMachine

[CompilerGenerated]
[StructLayout(LayoutKind.Auto)]
internal struct MultiCallMethodAsyncStateMachine : IAsyncStateMachine
{
    public int State;
    public AsyncTaskMethodBuilder<int> Builder;
    public int Arg0;
    public int Arg1;
    public int Arg2;
    public int Arg3;
    public int ResultOfAwait1;
    public int ResultOfAwait2;
    public int ResultToReturn;
    private TaskAwaiter<int> awaiter;

    void IAsyncStateMachine.MoveNext()
    {
        try
        {
            switch (this.State)
            {
                case -1:
                    HelperMethods.Before();
                    this.awaiter = AsyncMethods.MethodAsync(this.Arg0, this.Arg1).GetAwaiter();
                    if (!this.awaiter.IsCompleted)
                    {
                        this.State = 0;
                        this.Builder.AwaitUnsafeOnCompleted(ref this.awaiter, ref this);
                    }
                    break;
                case 0:
                    this.ResultOfAwait1 = this.awaiter.GetResult();
                    HelperMethods.Continuation1(this.ResultOfAwait1);
                    this.awaiter = AsyncMethods.MethodAsync(this.Arg2, this.Arg3).GetAwaiter();
                    if (!this.awaiter.IsCompleted)
                    {
                        this.State = 1;
                        this.Builder.AwaitUnsafeOnCompleted(ref this.awaiter, ref this);
                    }
                    break;
                case 1:
                    this.ResultOfAwait2 = this.awaiter.GetResult();
                    HelperMethods.Continuation2(this.ResultOfAwait2);
                    this.ResultToReturn = this.ResultOfAwait1 + this.ResultOfAwait2;
                    this.State = -2;
                    this.Builder.SetResult(this.ResultToReturn);
                    break;
            }
        }
        catch (Exception exception)
        {
            this.State = -2;
            this.Builder.SetException(exception);
        }
    }

    [DebuggerHidden]
    void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine)
    {
        this.Builder.SetStateMachine(stateMachine);
    }
}

6. 状态机与回调

状态机的核心思想是将代码按 await 拆分为多个部分,并通过回调链式执行。例如,MultiCallMethodAsync() 可以简化为以下形式:

internal static Task<int> MultiCallMethodAsync(int arg0, int arg1, int arg2, int arg3)
{
    TaskCompletionSource<int> taskCompletionSource = new TaskCompletionSource<int>();
    try
    {
        HelperMethods.Before();
        MethodAsync(arg0, arg1).ContinueWith(await1 =>
        {
            try
            {
                int resultOfAwait1 = await1.Result;
                HelperMethods.Continuation1(resultOfAwait1);
                MethodAsync(arg2, arg3).ContinueWith(await2 =>
                {
                    try
                    {
                        int resultOfAwait2 = await2.Result;
                        HelperMethods.Continuation2(resultOfAwait2);
                        int resultToReturn = resultOfAwait1 + resultOfAwait2;
                        taskCompletionSource.SetResult(resultToReturn);
                    }
                    catch (Exception exception)
                    {
                        taskCompletionSource.SetException(exception);
                    }
                });
            }
            catch (Exception exception)
            {
                taskCompletionSource.SetException(exception);
            }
        });
    }
    catch (Exception exception)
    {
        taskCompletionSource.SetException(exception);
    }
    return taskCompletionSource.Task;
}

7. 使用 Task.Yield()

Task.Yield() 可以强制方法异步完成:

internal static async Task YeildAsync()
{
    HelperMethods.Before();
    await Task.Yield(); // 立即返回,不等待 continuation 代码执行
    HelperMethods.Continuation(0);
}

其实现类似于:

internal static Task YeildAsync()
{
    TaskCompletionSource<object> taskCompletionSource = new TaskCompletionSource<object>();
    try
    {
        HelperMethods.Before();
        Task yeild = new Task(() => { });
        yeild.Start();
        yeild.ContinueWith(await =>
        {
            try
            {
                HelperMethods.Continuation(0);
                taskCompletionSource.SetResult(null);
            }
            catch (Exception exception)
            {
                taskCompletionSource.SetException(exception);
            }
        });
    }
    catch (Exception exception)
    {
        taskCompletionSource.SetException(exception);
    }
    return taskCompletionSource.Task;
}

总结

  • async / await 是语法糖:编译后会生成状态机,将代码按 await 拆分为多个部分,并通过回调链式执行。
  • 状态机:状态机负责管理异步操作的执行顺序和结果。
  • Task.Yield():强制方法异步完成,类似于 JavaScript 的 setTimeout()
kumakoko avatar
kumakoko
pure coder
comments powered by Disqus