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

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

static void Main(string[] args)
{
Console.WriteLine("\n进入Main()方法,执行线程ID:{0},来自线程池?{1},是背景线程?{2}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread, Thread.CurrentThread.IsBackground);
TestDoWorkAsync();
Console.WriteLine("\n返回Main()方法,等待用户敲击任意键退出,执行线程ID:{0},来自线程池?{1},是背景线程?{2}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread, Thread.CurrentThread.IsBackground);
Console.ReadKey();
} private async static void TestDoWorkAsync()
{
Console.WriteLine("\n进入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("\n退出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("\n使用TPL运行DoWork方法,负责执行的线程ID:{0},来自线程池?{1},是背景线程?{2}",
Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread,
Thread.CurrentThread.IsBackground);
Thread.Sleep();
return ;
});
}

看一下执行结果

可以看到 调用者线程在执行到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的线程同步上下文就是新线程而不是调用者线程)

C#5.0 异步编程 Async和Await--理解异步方法与线程之间的关系的更多相关文章

  1. C#异步编程(async and await)及异步方法同步调用

    1.什么是异步? 异步操作通常用于执行完成时间可能较长的任务,如打开大文件.连接远程计算机或查询数据库=异步操作在主应用程序线程以外的线程中执行.应用程序调用方法异步执行某个操作时,应用程序可在异步方 ...

  2. C#5.0 异步编程async/await用法

    微软在发布VS2012的同时推出了C#5.0,其中包含了async和await 代码如下: class Program { private static readonly Stopwatch watc ...

  3. .NET4.5 异步编程 async和await

    msdn介绍:https://msdn.microsoft.com/zh-cn/library/hh191443.aspx 其实很简单,标记了async的方法为异步方法,从方法的左大括号开始同步执行, ...

  4. 异步编程- async和await

    使用目的 避免阻塞主线程 提高程序响应能力 C#中使用 C# 中的 Async 和 Await 关键字是异步编程的核心. 疑惑 The async and await keywords don't c ...

  5. C#中的异步编程Async 和 Await

    谈到C#中的异步编程,离不开Async和Await关键字 谈到异步编程,首先我们就要明白到底什么是异步编程. 平时我们的编程一般都是同步编程,所谓同步编程的意思,和我们平时说的同时做几件事情完全不同. ...

  6. .net异步编程async和await的讨论收获

    微软官方描述: C# 5 引入了一种简便方法,即异步编程.此方法利用了 .NET Framework 4.5 及更高版本..NET Core 和 Windows 运行时中的异步支持. 编译器可执行开发 ...

  7. C#5.0 异步编程 Async和Await--异步方法的规范和注意事项

    要些异步方法要注意一下几点: 异步方法的返回值有三种: 1.没有任何返回值的void 2.返回一个Task任务的Task,可以获得该异步方法的执行状态 3.返回Task<T> 可以获得异步 ...

  8. C#5.0 异步编程 Async和Await--介绍

    C#5.0引入async和await关键字实现方法的异步调用. 直接进入正题. async只是一个标识符,并没有实际的用途,只是用于表明某个方法是异步方法,在方法前面加上async 表示该方法为一个异 ...

  9. C#异步编程----async和await组合的写法

    微软示例: private async void StartButton_Click(object sender, RoutedEventArgs e) { // ExampleMethodAsync ...

随机推荐

  1. SqlServer表名称定义

    每一个数据表 添加一个 扩展 属性:Description  填写表描述. 查看是否所有表都添加的Sql如下: SELECT a.name AS name, g.[value] FROM sys.ta ...

  2. 设计模式--简单工厂(Simple Factory)

    工厂模式是最常用的一种创建型模式,通常所说的工厂模式一般是指工厂方法模式.本篇是是工厂方法模式的“小弟”,我们可以将其理解为工厂方法模式的预备知识,它不属于GoF 23种设计模式,但在软件开发中却也应 ...

  3. setmetamode - define the keyboard meta key handling

    总览 setmetamode [ meta|bit|metabit | esc|prefix|escprefix ] 描述 没有参数时, setmetamode 将打印当前 Meta 键模式; 有参数 ...

  4. TCP/UDP Linux网络编程详解

    本文主要记录TCP/UDP网络编程的基础知识,采用TCP/UDP实现宿主机和目标机之间的网络通信. 内容目录 1. 目标2.Linux网络编程基础2.1 嵌套字2.2 端口2.3 网络地址2.3.1 ...

  5. Linux Shell脚本在service模式下的环境变量

    普通Shell脚本在终端下运行时,环境变量来自于init/profile/.bash_profile等,就是在当前终端下可以得到的环境,比如PATH.在service模式下,脚本运行在受限的环境中:比 ...

  6. BZOJ 2238: Mst DFS序+KDtree

    明明可以用二维数点来做啊,网上为什么都是树剖+线段树呢 ? code: #include <cstdio> #include <cstring> #include <al ...

  7. Mysql 数据库默认值选 ''" 、Null和Empty String的区别

    两者的查询方式不一样:NULL值查询使用is null/is not null查询,而empty string可以使用=或者!=.<.>等算术运算符,这点算是最主要的区别了. 对于myis ...

  8. IDEA使用Maven搭建JavaWeb项目

    1. 新建项目 2. 填写项目坐标信息 3. 手动生效pom.xml 4. 添加maven常用配置 1) 设置项目统一编码 <!-- 设置编码 --> <properties> ...

  9. Struts2 简单的增删改查

    1:主页面 <a href="emp-list">emp-list</a> <br> 然后到struts.xml文件中找到对应的emp-list ...

  10. delphi 将Dll等生成资源文件

    资源文件一般为扩展名为res的文件,其自带的资源编译工具BRCC32.EXE(位于/Delphi/BIN目录下) 1.编写rc脚本文本用记事本或其它文本编辑器编写一个扩展名为rc的文件,格式分别为在资 ...