NetCore高级系列文章04---async、await原理揭秘

一、async、await本质

直接说结论:它们是C#提供的语法糖,编译器编译后是状态机的调用。

先看如下的一段代码,要main方法中调用了三个await方法

 将此dll进行反编译为4.0的代码如下:

 可见到两个Main方法,也就是说我们在程序中Main方法上加了async关键词,编译器会编译成一个是异步的一个是非异步方法,程序还是将非异步的方法作为入口函数。进入函数

在该函数中调了异步的Main方法,再进入:

 在该函数中创建了一个状态机,将参数传给状态机,并调用期Start方法,可知异步方法实际上是状态机方法的调用

进入状态机类型<Main>d__0

private sealed class <Main>d__0 : IAsyncStateMachine
{
    public int <>1__state;

    public AsyncTaskMethodBuilder <>t__builder;

    public string[] args;

    private string <text>5__1;

    private HttpClient <httpClient>5__2;

    private string <html>5__3;

    private string <>s__4;

    private string <>s__5;

    private TaskAwaiter<string> <>u__1;

    private TaskAwaiter <>u__2;

    private void MoveNext()
    {
        int num = <>1__state;
        try
        {
            TaskAwaiter awaiter2;
            TaskAwaiter<string> awaiter;
            switch (num)
            {
            default:
                <httpClient>5__2 = new HttpClient();
                goto case 0;
            case 0:
                try
                {
                    TaskAwaiter<string> awaiter3;
                    if (num != 0)
                    {
                        awaiter3 = <httpClient>5__2.GetStringAsync("https://www.baidu.com").GetAwaiter();
                        if (!awaiter3.IsCompleted)
                        {
                            num = (<>1__state = 0);
                            <>u__1 = awaiter3;
                            <Main>d__0 stateMachine = this;
                            <>t__builder.AwaitUnsafeOnCompleted(ref awaiter3, ref stateMachine);
                            return;
                        }
                    }
                    else
                    {
                        awaiter3 = <>u__1;
                        <>u__1 = default(TaskAwaiter<string>);
                        num = (<>1__state = -1);
                    }
                    <>s__4 = awaiter3.GetResult();
                    <html>5__3 = <>s__4;
                    <>s__4 = null;
                    Console.WriteLine(<html>5__3);
                    <html>5__3 = null;
                }
                finally
                {
                    if (num < 0 && <httpClient>5__2 != null)
                    {
                        ((IDisposable)<httpClient>5__2).Dispose();
                    }
                }
                <httpClient>5__2 = null;
                awaiter2 = File.WriteAllTextAsync("E:\\test.txt", "zhengwei").GetAwaiter();
                if (!awaiter2.IsCompleted)
                {
                    num = (<>1__state = 1);
                    <>u__2 = awaiter2;
                    <Main>d__0 stateMachine = this;
                    <>t__builder.AwaitUnsafeOnCompleted(ref awaiter2, ref stateMachine);
                    return;
                }
                goto IL_015f;
            case 1:
                awaiter2 = <>u__2;
                <>u__2 = default(TaskAwaiter);
                num = (<>1__state = -1);
                goto IL_015f;
            case 2:
                {
                    awaiter = <>u__1;
                    <>u__1 = default(TaskAwaiter<string>);
                    num = (<>1__state = -1);
                    break;
                }
                IL_015f:
                awaiter2.GetResult();
                Console.WriteLine("写入成功");
                awaiter = File.ReadAllTextAsync("E:\\test.txt").GetAwaiter();
                if (!awaiter.IsCompleted)
                {
                    num = (<>1__state = 2);
                    <>u__1 = awaiter;
                    <Main>d__0 stateMachine = this;
                    <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine);
                    return;
                }
                break;
            }
            <>s__5 = awaiter.GetResult();
            <text>5__1 = <>s__5;
            <>s__5 = null;
            Console.WriteLine("文件内容" + <text>5__1);
        }
        catch (Exception exception)
        {
            <>1__state = -2;
            <text>5__1 = null;
            <>t__builder.SetException(exception);
            return;
        }
        <>1__state = -2;
        <text>5__1 = null;
        <>t__builder.SetResult();
    }

    void IAsyncStateMachine.MoveNext()
    {
        //ILSpy generated this explicit interface implementation from .override directive in MoveNext
        this.MoveNext();
    }

    [DebuggerHidden]
    private void SetStateMachine(IAsyncStateMachine stateMachine)
    {
    }

    void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine)
    {
        //ILSpy generated this explicit interface implementation from .override directive in SetStateMachine
        this.SetStateMachine(stateMachine);
    }
}

在此状态机类中可以看到我们自己写的Main方法中的主要代码已编译到了方法MoveNext()中,并将我们的局部变量编译成了成员变量

方法中有一个case,我们自己写的三个异步方法被编译后拆到了多个case中去了,MoveNext方法会根据不同的num被多次调用

编译的代码中可见到在每次调用异步方法时都会去判断是否完成

 

 

 

编译后的代码意思是当执行到string html = await httpClient.GetStringAsync("https://www.baidu.com");时,如果没有完成就直接返回了,并不会再往下执行

只有当此段代码执行完成后会继续往下执行,当执行到第二个异步方法await File.WriteAllTextAsync(@"E:\test.txt","zhengwei")时又会返回,等待执行完后再往下执行

同理,执行第三步异步方法var text = await File.ReadAllTextAsync(@"E:\test.txt");也是如此

所有异步方法都 执行完成后,会break;跳出状态机。

未完待续。。。

二、async背后的线程切换

await调用的等待期间,.net会把当前的线程返回线线程池,等异步方法调用执行完毕后,框架会从线程池再取出来一个线程执行后续的代码。

代码验证

 运行的结果可知在这个方法中使用的并不是同一个进程,一个是1,一个是9

当然,有时写入的内容少,也有可能方法开始和结束的线程id是一样的, 

三、异步方法与多线程的关系

异步方法的代码并不会自动在新的线程中执行,除非把代码放到新线程中执行。

在方法调用前和方法调用中用的是同一个线程,都是1,并没有开启一个新的线程

 

 

除非把这个异步方法放到一个新的线程中:可以看到方法调用中的线程为4,调用前的线程为1。

 

 

 

四、CancellationToken在异步方法中的使用

 

posted @ 2023-12-28 15:00  爱生活,爱代码  阅读(499)  评论(0编辑  收藏  举报