zoukankan      html  css  js  c++  java
  • 第十七节:从状态机的角度async和await的实现原理(新)

    一. 深度剖析

    准备:

      先给VS安装一个插件ILSpy,这样更容易反编译代码进行查看,另外要注意反编译async和await的时候,要把C#代码版本改为4.0哦。

    1.什么是状态机

     (1).含义:通常我们所说的状态机(State Machine)指的是有限状态自动机的简称,是现实事物运行规则抽象而成的一个数学模型,可以理解成一个状态转换图。(状态机是计算机科学的重要基础概念之一,也可以说是一种总结归纳问题的思想,应用范围非常广泛)

     (2).例子:自动门有两个状态,open 和 closed ,closed 状态下,如果读取开门信号,那么状态就会切换为 open 。open 状态下如果读取关门信号,状态就会切换为 closed .

     (3).涉及到4个相关概念:

      A.状态(State):一个状态机至少包括两个状态.(例如上面自动门的例子,有 open 和 closed 两个状态。)

      B.事件(Event):事件就是执行某个操作的触发条件或者口令.(对于自动门,“按下开门按钮”就是一个事件。)

      C.动作(Action):事件发生以后要执行的动作,一个action对应一个函数.(事件是“按开门按钮”,动作是“开门”)

      D.变换(Transition):从一个状态转换成另外一个状态.(“开门过程”就是一个变换。)

     (4). C#的状态机提供了IAsyncStateMachine接口,里面有MoveNext 和 SetStateMachine方法处理相应业务.

    2. 状态机分析

      async关键字标记方法是一个异步方法,编译器通过这个标记去改造这个方法体为创建状态机的方法。await是关键字是为了实现状态机中的一个状态, 每当有一个await,就会生成一个对应的状态。状态机就是根据这个状态,去一步步的调用异步委托,然后回调,包括状态机的解析。

    (1).状态机的默认状态都是-1, 结束状态都是-2.

    (2).每await一次就会产生一个 TaskAwaiter<int> awaiter; 改变状态机的状态, 当有多个await的时候,每个await都会改变状态机的状态,比如 改为 0,1,2,3,4 等等, 分别表示 代码中await xxx 这句话执行完成。

    (3).状态机的执行套路:

      A. 首先创建一个 <xxx>d_num 的方法, xxx代表方法名,num可能是0,1,2,3等,实现IAsyncStateMachine接口。

      B. 在MoveNext方法中, 源代码中每个 await xxxx 都会对应生成是一个 TaskAwaiter<int> awaiter,然后 xxxx.GetAwaiter()

      C. 判断状态机是否执行完if (!awaiter.IsCompleted),没有执行完的话走 <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine); 代表释放当前线程

      D. 执行完后走,<>s__1 = awaiter.GetResult(); 拿到返回值,继续走后面的代码。

    (此处写的比较抽象,看下面3 结合代码编译再分析)

    3. 结合代码编译分析

    前提:准备1个Index方法,我们把它当做主方法,在该方法里面调用 F1Async-F5Async这五个方法. (要补充截图这里)

    代码分享:

     public class Home2Controller : Controller
        {
    
            /// <summary>
            /// 该方法为主方法,用于调用下面的F1-F5的方法
            /// </summary>
            /// <returns></returns>
            public async Task<IActionResult> Index()
            {
                await F1Async();
                await F2Async();
                await F3Async();
                await F4Async();
                await F5Async();
    
                return View();
            }
    
            /// <summary>
            /// 没有加async和await的方法
            /// (也是一个计算密集型的异步方法,只是编译的时候本身不会被编译成状态机)
            /// </summary>
            /// <returns></returns>
            public static Task<int> F1Async()
            {
                return Task.Run(() =>
                {
                    return 2;
                });
            }
    
            /// <summary>
            /// 只要标记了async 就会被编译成状态机
            /// 如果方法声明为 async,那么可以直接 return 具体的值,不再用创建Task,由编译器创建 Task: 
            /// </summary>
            /// <returns></returns>
            public static async Task<int> F2Async()
            {
                return 2;
            }
    
            /// <summary>
            /// 计算密集型的异步方法
            /// (方法本身也会被编译成状态机)
            /// </summary>
            /// <returns></returns>
            public static async Task<int> F3Async()
            {
                return await Task.Run(() =>
                {
                    return 2;
                });
            }
    
            /// <summary>
            /// I/O密集型的异步方法
            /// </summary>
            /// <returns></returns>
            public async Task<int> F4Async()
            {
                AsyncDBContext context = new AsyncDBContext();
                for (int i = 0; i < 10000; i++)
                {
                    UserInfor uInfor = new UserInfor()
                    {
                        id = Guid.NewGuid().ToString("N"),
                        userName = "ypf",
                        addTime = DateTime.Now
                    };
                    await context.AddAsync(uInfor);
                }
                return await context.SaveChangesAsync();
            }
    
    
          /// <summary>
          /// 没有创建状态机,但是new 了1个新的 task
          /// </summary>
          /// <returns></returns>
           public static Task<int> F5Async()
            {
                //内部是new Task<TResult>(result)
                return Task.FromResult(3);
            }
    
        }
    View Code

     (1).F1Async:没有加async和await,但它本身也是一个计算密集型的异步方法,该方法本身不会被编译成状态机,但调用它的方法Index会被编译成状态机。

     (2).F2Async:只加了async,会生成状态机,但由于没有加await所以不会涉及到中间状态的变化,从-1默认状态  变为 结束的-2状态。

    代码分享:

     1   public class Home2Controller : Controller
     2     {
     3 
     4         /// <summary>
     5         /// 该方法为主方法,用于调用下面的F1-F5的方法
     6         /// </summary>
     7         /// <returns></returns>
     8         public async Task<IActionResult> Index()
     9         {
    10             await F1Async();
    11             await F2Async();
    12             await F3Async();
    13             await F4Async();
    14             await F5Async();
    15 
    16             return View();
    17         }
    18 
    19         /// <summary>
    20         /// 没有加async和await的方法
    21         /// (也是一个计算密集型的异步方法,只是编译的时候本身不会被编译成状态机)
    22         /// </summary>
    23         /// <returns></returns>
    24         public static Task<int> F1Async()
    25         {
    26             return Task.Run(() =>
    27             {
    28                 return 2;
    29             });
    30         }
    31 
    32         /// <summary>
    33         /// 只要标记了async 就会被编译成状态机
    34         /// 如果方法声明为 async,那么可以直接 return 具体的值
    35         /// <returns></returns>
    36         public static async Task<int> F2Async()
    37         {
    38             return 2;
    39         }
    40 
    41         /// <summary>
    42         /// 计算密集型的异步方法
    43         /// (方法本身也会被编译成状态机)
    44         /// </summary>
    45         /// <returns></returns>
    46         public static async Task<int> F3Async()
    47         {
    48             return await Task.Run(() =>
    49             {
    50                 return 2;
    51             });
    52         }
    53 
    54         /// <summary>
    55         /// I/O密集型的异步方法
    56         /// </summary>
    57         /// <returns></returns>
    58         public async Task<int> F4Async()
    59         {
    60             AsyncDBContext context = new AsyncDBContext();
    61             for (int i = 0; i < 10000; i++)
    62             {
    63                 UserInfor uInfor = new UserInfor()
    64                 {
    65                     id = Guid.NewGuid().ToString("N"),
    66                     userName = "ypf",
    67                     addTime = DateTime.Now
    68                 };
    69                 await context.AddAsync(uInfor);
    70             }
    71             return await context.SaveChangesAsync();
    72         }
    73 
    74 
    75       /// <summary>
    76       /// 没有创建状态机,但是new 了1个新的 task
    77       /// </summary>
    78       /// <returns></returns>
    79        public static Task<int> F5Async()
    80         {
    81             //内部是new Task<TResult>(result)
    82             return Task.FromResult(3);
    83         }
    84 
    85     }
    View Code

    核心代码剖析:

     

     (3).F3Async:既有async也有await (await只有1个),该方法是使用了Task.Run,我们把它归为计算型的异步方法。

    代码分享:

            [AsyncStateMachine(typeof(<F3Async>d__3))]
        [DebuggerStepThrough]
        public static Task<int> F3Async()
        {
            <F3Async>d__3 stateMachine = new <F3Async>d__3();
            stateMachine.<>t__builder = AsyncTaskMethodBuilder<int>.Create();
            stateMachine.<>1__state = -1;
            AsyncTaskMethodBuilder<int> <>t__builder = stateMachine.<>t__builder;
            <>t__builder.Start(ref stateMachine);
            return stateMachine.<>t__builder.Task;
        }
            [CompilerGenerated]
        private sealed class <F3Async>d__3 : IAsyncStateMachine
        {
            public int <>1__state;
    
            public AsyncTaskMethodBuilder<int> <>t__builder;
    
            private int <>s__1;
    
            private TaskAwaiter<int> <>u__1;
    
            private void MoveNext()
            {
                int num = <>1__state;
                int result;
                try
                {
                    TaskAwaiter<int> awaiter;
                    if (num != 0)
                    {
                        awaiter = Task.Run(() => 2).GetAwaiter();
                        if (!awaiter.IsCompleted)
                        {
                            num = (<>1__state = 0);
                            <>u__1 = awaiter;
                            <F3Async>d__3 stateMachine = this;
                            <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine);
                            return;
                        }
                    }
                    else
                    {
                        awaiter = <>u__1;
                        <>u__1 = default(TaskAwaiter<int>);
                        num = (<>1__state = -1);
                    }
                    <>s__1 = awaiter.GetResult();
                    result = <>s__1;
                }
                catch (Exception exception)
                {
                    <>1__state = -2;
                    <>t__builder.SetException(exception);
                    return;
                }
                <>1__state = -2;
                <>t__builder.SetResult(result);
            }
    
            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);
            }
        }
    View Code

    核心代码剖析:

     (4).F4Async:既有async又有await,且两个await,两个await按照顺序执行。

    代码分享:

            [AsyncStateMachine(typeof(<F4Async>d__4))]
        [DebuggerStepThrough]
        public Task<int> F4Async()
        {
            <F4Async>d__4 stateMachine = new <F4Async>d__4();
            stateMachine.<>4__this = this;
            stateMachine.<>t__builder = AsyncTaskMethodBuilder<int>.Create();
            stateMachine.<>1__state = -1;
            AsyncTaskMethodBuilder<int> <>t__builder = stateMachine.<>t__builder;
            <>t__builder.Start<<F4Async>d__4>(ref stateMachine);
            return stateMachine.<>t__builder.get_Task();
        }
    [CompilerGenerated]
        private sealed class <F4Async>d__4 : IAsyncStateMachine
        {
            public int <>1__state;
    
            public AsyncTaskMethodBuilder<int> <>t__builder;
    
            public Home2Controller <>4__this;
    
            private AsyncDBContext <context>5__1;
    
            private int <i>5__2;
    
            private UserInfor <uInfor>5__3;
    
            private int <>s__4;
    
            private ValueTaskAwaiter<EntityEntry<UserInfor>> <>u__1;
    
            private TaskAwaiter<int> <>u__2;
    
            private void MoveNext()
            {
            
                int num = <>1__state;
                int result;
                try
                {
                    ValueTaskAwaiter<EntityEntry<UserInfor>> awaiter;
                    if (num == 0)
                    {
                        awaiter = <>u__1;
                        <>u__1 = default(ValueTaskAwaiter<EntityEntry<UserInfor>>);
                        num = (<>1__state = -1);
                        goto IL_00e8;
                    }
                    if (num != 1)
                    {
                        <context>5__1 = new AsyncDBContext();
                        <i>5__2 = 0;
                        goto IL_010a;
                    }
                    TaskAwaiter<int> awaiter2 = <>u__2;
                    <>u__2 = default(TaskAwaiter<int>);
                    num = (<>1__state = -1);
                    goto IL_0188;
                    IL_00e8:
                    awaiter.GetResult();
                    <uInfor>5__3 = null;
                    <i>5__2++;
                    goto IL_010a;
                    IL_010a:
                    if (<i>5__2 < 10000)
                    {
                        <uInfor>5__3 = new UserInfor
                        {
                            id = Guid.NewGuid().ToString("N"),
                            userName = "ypf",
                            addTime = DateTime.Now
                        };
                        awaiter = ((DbContext)<context>5__1).AddAsync<UserInfor>(<uInfor>5__3, default(CancellationToken)).GetAwaiter();
                        if (!awaiter.get_IsCompleted())
                        {
                            num = (<>1__state = 0);
                            <>u__1 = awaiter;
                            <F4Async>d__4 stateMachine = this;
                            <>t__builder.AwaitUnsafeOnCompleted<ValueTaskAwaiter<EntityEntry<UserInfor>>, <F4Async>d__4>(ref awaiter, ref stateMachine);
                            return;
                        }
                        goto IL_00e8;
                    }
                    awaiter2 = ((DbContext)<context>5__1).SaveChangesAsync(default(CancellationToken)).GetAwaiter();
                    if (!awaiter2.get_IsCompleted())
                    {
                        num = (<>1__state = 1);
                        <>u__2 = awaiter2;
                        <F4Async>d__4 stateMachine = this;
                        <>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<int>, <F4Async>d__4>(ref awaiter2, ref stateMachine);
                        return;
                    }
                    goto IL_0188;
                    IL_0188:
                    <>s__4 = awaiter2.GetResult();
                    result = <>s__4;
                }
                catch (Exception exception)
                {
                    <>1__state = -2;
                    <context>5__1 = null;
                    <>t__builder.SetException(exception);
                    return;
                }
                <>1__state = -2;
                <context>5__1 = null;
                <>t__builder.SetResult(result);
            }
    
            [DebuggerHidden]
            private void SetStateMachine(IAsyncStateMachine stateMachine)
            {
            }
        }
    View Code

    核心代码剖析:

     (5).F5Async:没有async和await,没有创建状态机,但是new 了1个新的 task。

     (6).Index:多个await,通过goto 一步一步跳转,按顺序执行。

    代码分享:

    [CompilerGenerated]
        private sealed class <Index>d__0 : IAsyncStateMachine
        {
            public int <>1__state;
    
            public AsyncTaskMethodBuilder<IActionResult> <>t__builder;
    
            public Home2Controller <>4__this;
    
            private TaskAwaiter<int> <>u__1;
    
            private void MoveNext()
            {
                
                int num = <>1__state;
                IActionResult result;
                try
                {
                    TaskAwaiter<int> awaiter5;
                    TaskAwaiter<int> awaiter4;
                    TaskAwaiter<int> awaiter3;
                    TaskAwaiter<int> awaiter2;
                    TaskAwaiter<int> awaiter;
                    switch (num)
                    {
                    default:
                        awaiter5 = F1Async().GetAwaiter();
                        if (!awaiter5.get_IsCompleted())
                        {
                            num = (<>1__state = 0);
                            <>u__1 = awaiter5;
                            <Index>d__0 stateMachine = this;
                            <>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<int>, <Index>d__0>(ref awaiter5, ref stateMachine);
                            return;
                        }
                        goto IL_0091;
                    case 0:
                        awaiter5 = <>u__1;
                        <>u__1 = default(TaskAwaiter<int>);
                        num = (<>1__state = -1);
                        goto IL_0091;
                    case 1:
                        awaiter4 = <>u__1;
                        <>u__1 = default(TaskAwaiter<int>);
                        num = (<>1__state = -1);
                        goto IL_00f3;
                    case 2:
                        awaiter3 = <>u__1;
                        <>u__1 = default(TaskAwaiter<int>);
                        num = (<>1__state = -1);
                        goto IL_0155;
                    case 3:
                        awaiter2 = <>u__1;
                        <>u__1 = default(TaskAwaiter<int>);
                        num = (<>1__state = -1);
                        goto IL_01bd;
                    case 4:
                        {
                            awaiter = <>u__1;
                            <>u__1 = default(TaskAwaiter<int>);
                            num = (<>1__state = -1);
                            break;
                        }
                        IL_01bd:
                        awaiter2.GetResult();
                        awaiter = F5Async().GetAwaiter();
                        if (!awaiter.get_IsCompleted())
                        {
                            num = (<>1__state = 4);
                            <>u__1 = awaiter;
                            <Index>d__0 stateMachine = this;
                            <>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<int>, <Index>d__0>(ref awaiter, ref stateMachine);
                            return;
                        }
                        break;
                        IL_0091:
                        awaiter5.GetResult();
                        awaiter4 = F2Async().GetAwaiter();
                        if (!awaiter4.get_IsCompleted())
                        {
                            num = (<>1__state = 1);
                            <>u__1 = awaiter4;
                            <Index>d__0 stateMachine = this;
                            <>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<int>, <Index>d__0>(ref awaiter4, ref stateMachine);
                            return;
                        }
                        goto IL_00f3;
                        IL_0155:
                        awaiter3.GetResult();
                        awaiter2 = <>4__this.F4Async().GetAwaiter();
                        if (!awaiter2.get_IsCompleted())
                        {
                            num = (<>1__state = 3);
                            <>u__1 = awaiter2;
                            <Index>d__0 stateMachine = this;
                            <>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<int>, <Index>d__0>(ref awaiter2, ref stateMachine);
                            return;
                        }
                        goto IL_01bd;
                        IL_00f3:
                        awaiter4.GetResult();
                        awaiter3 = F3Async().GetAwaiter();
                        if (!awaiter3.get_IsCompleted())
                        {
                            num = (<>1__state = 2);
                            <>u__1 = awaiter3;
                            <Index>d__0 stateMachine = this;
                            <>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<int>, <Index>d__0>(ref awaiter3, ref stateMachine);
                            return;
                        }
                        goto IL_0155;
                    }
                    awaiter.GetResult();
                    result = <>4__this.View();
                }
                catch (Exception exception)
                {
                    <>1__state = -2;
                    <>t__builder.SetException(exception);
                    return;
                }
                <>1__state = -2;
                <>t__builder.SetResult(result);
            }
    
            [DebuggerHidden]
            private void SetStateMachine(IAsyncStateMachine stateMachine)
            {
            }
        }
    View Code

    核心代码剖析:

    4. 重点比较一下:F1Async 和 F3Async 的区别

     (1).F1Async和F3Async都是异步方法,在外层Index方法中调用的时候,都要加await,就外层而言都不会卡主线程,外层方法都会被编译成状态机。

     (2).从编译的角度而言 F1Async 方法本身不会被编译成状态机,F3Async方法本身会被编译成状态机。

    5. 再次扩展

     (1).等待的时候要用 await xxxAsync, 而不要用 xxxAsync.wait() 和 .Result

     (2).等待多个用 await Task.WhenAll 而不要用 Task.WaitAll

    原因?

     后者是同步写法啊,阻塞线程的,从上面的编译的源码可以看出来,没有await不会生成TaskAwaiter<int> awaiter。

     

    二. 几个用法

     1. 异常捕获

     代码1

    public static async void EmailAsync() {
        List<string> addrs = new List<string>();
        IEnumerable<Task> asyncOps = addrs.Select(addr => SendMailAsync(addr));
        try {
            await Task.WhenAll(asyncOps);
        } catch (AggregateException ex) {
            // 可以通过 InnerExceptions 来得到内部返回的异常
            var exceptions = ex.InnerExceptions;
            // 也可以使用 Handle 对每个异常进行处理
            ex.Handle(innerEx => {
                // 此处的演示仅仅为了说明 ex.Handle 可以对异常进行单独处理
                // 实际项目中不一定会抛出此异常
    
                if (innerEx is OperationCanceledException oce) {
                    // 对 OperationCanceledException 进行单独的处理
                    return true;
                } else if (innerEx is UnauthorizedAccessException uae) {
                    // 对 UnauthorizedAccessException 进行单独处理
                    return true;
                }
                return false;
            });
        }
    }

    代码2

    public static async void EmailAsync() {
        List<string> addrs = new List<string>();
        IEnumerable<Task> asyncOps = addrs.Select(addr => SendMailAsync(addr));
        try {
            await Task.WhenAll(asyncOps);
        } catch (AggregateException ex) {
            // 此处可以针对每个任务进行更加具体的管理
            foreach (Task<string> task in asyncOps) {
                if (task.IsCanceled) {
                }else if (task.IsFaulted) {
                }else if (task.IsCompleted) {
                }
            }
        }
    }

    代码3

    try
    {
        HttpClient hc = new HttpClient();
        var task1 = hc.GetStringAsync(textBox1.Text);
        var task2 = hc.GetStringAsync(textBox2.Text);
        var task3 = hc.GetStringAsync(textBox3.Text);
        Task.WaitAll(task1, task2, task3);
        label1.Text = task1.Result.Length.ToString();
        label2.Text = task2.Result.Length.ToString();
        label3.Text = task3.Result.Length.ToString();
    }
    catch (AggregateException ae)
    {
        MessageBox.Show(ae.GetBaseException().ToString());
    }

    !

    • 作       者 : Yaopengfei(姚鹏飞)
    • 博客地址 : http://www.cnblogs.com/yaopengfei/
    • 声     明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
    • 声     明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
     
  • 相关阅读:
    [Qt] 文本文件读写, 摘自官方文档
    [Windows] Socket Server Failed to bind, error 10048
    lodctr /R 失败的情况
    ModuleNotFoundError: No module named 'sklearn.cross_validation'
    [Qt] 通过socket将另一个程序的某个窗口调到最前端
    SortedDictionary<TKey, TValue> 类 表示根据键进行排序的键/值对的集合。
    finally不管有没有错都会运行 finally 块用于清除 try 块中分配的任何资源,以及运行任何即使在发生异常时也必须执行的代码
    HttpWebRequest使用证书请求
    string StartsWith 方法 Https
    设置https验证方式
  • 原文地址:https://www.cnblogs.com/yaopengfei/p/12848795.html
Copyright © 2011-2022 走看看