从状态机的角度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);
        }

    }

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

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值