zoukankan      html  css  js  c++  java
  • C#5.0 异步编程 Async和Await--理解异步方法与线程之间的关系

    这次来理解一下异步方法与线程之间的关系

    新建一个控制台程序 代码如下

    static void Main(string[] args)
            {
                Console.WriteLine("
    进入Main()方法,执行线程ID:{0},来自线程池?{1},是背景线程?{2}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread, Thread.CurrentThread.IsBackground);
                TestDoWorkAsync();
                Console.WriteLine("
    返回Main()方法,等待用户敲击任意键退出,执行线程ID:{0},来自线程池?{1},是背景线程?{2}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread, Thread.CurrentThread.IsBackground);
                Console.ReadKey();
            }
    
            private async static void TestDoWorkAsync()
            {
                Console.WriteLine("
    进入TestDoWorkAsync()方法,await语句之前的代码执行线程ID:{0},来自线程池?{1},是背景线程?{2}",Thread.CurrentThread.ManagedThreadId,Thread.CurrentThread.IsThreadPoolThread,Thread.CurrentThread.IsBackground);
                //await之前的代码是调用者线程来执行,await之后到下一个await之前的代码由线程池中的同一个线程执行
                //但是在在UI程序中,UI线程调用了async方法,则await之后的语句由UI线程来执行,不由线程池中的线程来执行
                //TODO 当我们使用await等待一个异步操作时,默认情况下,它会捕获当前线程的同步上下文,等待异步方法执行结束,其后继的代码会被打包到一起,调用SyncContext.Post方法,推送到前面的同步线程上下文中执行
                //但是在await后面的语句调用这个方法并传参为false await DoWork().ConfigureAwait(false);可以通知系统不要捕获线程同步上下文,不会被post到await之前捕获的线程上下文中执行,而是直接使用当前完成异步任务的那个线程执行,避免了线程切换
                int result = await DoWork();
                Console.WriteLine("
    退出TestDoWorkAsync()方法,await语句之后的代码执行线程ID:{0},来自线程池?{1},是背景线程?{2}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread, Thread.CurrentThread.IsBackground);
                Console.WriteLine("结果为{0}",result);
            }
    
            static Task<int> DoWork()
            {
                
                return Task.Run(() =>
                {
                    Console.WriteLine("
    使用TPL运行DoWork方法,负责执行的线程ID:{0},来自线程池?{1},是背景线程?{2}",
                        Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread,
                        Thread.CurrentThread.IsBackground);
                    Thread.Sleep(2000);
                    return 5;
                });
            }

    看一下执行结果

    可以看到 调用者线程在执行到await这里时会开启一个新的线程去执行await方法,并且立即返回,所以在await DoWork();方法前和TestDoWorkAsync();方法后都是由主线程去执行,而异步方法DoWork()和DoWork之后的语句都是由新线程去执行

    看一下调用过程

                                  

     小结:同一个方法中的代码,以await为边界,被划分为两块或者多块(取决于await语句有多少个),然后,会由线程池中的某个线程来负责执行它们

     

    但是在涉及到UI线程时又会有些变化,这也是让我很难理解的一点

    比如在按钮响应事件中调用这个方法

    private async void UseAsync()
            {
                lblInfo.Text = "等待后台程序完成";
                btnLanch.Enabled = false;
                string result = await SayHelloToAsync("张三");
                
                lblInfo.Text = result;
                btnLanch.Enabled = true;
            }
    
            private Task<string> SayHelloToAsync(string name)
            {
                return Task.Factory.StartNew(() => SayHelloTo(name));
            }

    这里在执行完SayHelloToAsync()方法之后,UI控件会得到更新,淫威UseAsync方法的调用是在用户单击按钮引发的,是在UI线程中启动的异步调用,所以在执行完之后,后面的代码会被推送到UI线程中会执行

    当我们使用await等待一个异步操作时,默认情况下,它会捕获当前线程的同步上下文,等待异步方法执行结束,其后继的代码会被打包到一起,
    调用SyncContext.Post方法,推送到前面的同步线程上下文中执行
    根据我的实践,这种情况只会出现在有UI界面的的程序中,在控制台程序中,await异步执行完之后,后面的代码还是会由新线程执行,不会由调用者线程执行。
    但是感觉这里有坑,先挖在这,之后再填。(我猜测在控制台程序中,await的线程同步上下文就是新线程而不是调用者线程)
  • 相关阅读:
    状态模式
    简单密码再次加密
    服务层定义自己的服务异常类
    必备网络基础知识(持续补充)
    MongoDB基础入门
    Git命令整理
    算法基础(四)
    RabbitMQ消息队列
    设计模式(23种)
    二叉树知识点
  • 原文地址:https://www.cnblogs.com/c-supreme/p/8974416.html
Copyright © 2011-2022 走看看