1.Await为什么不会导致堵塞

我们都知道Await关键字是.Net FrameWork4.5引入的特性。await使得我们使用异步更加时特别便捷,并且还不会导致线程堵塞。我们在使用时也就莫名其妙的使用。往往不知道为什么不会导致线程堵塞。在这里,简单的谈论下await的一点原理。

在c#并行编程这本书中是这么介绍await的:async方法在开始时以同步方式执行,在async方法内部,await关键字对它参数执行一个异步等待,它首先检查操作是否已经完成,如果完成,就继续运行(同步方式),否则,会暂停async方法,并返回.留下一个未完成的task,一段时间后,操作完成,async方法就恢复执行.

看到这句话应该就差不多能想到await为什么不会导致线程堵塞了,当碰到await时如果没有执行成功就先暂停这个方法的执行,执行方法外以下代码,等await操作完成后再执行这个方法await之后的代码。下面以一个例子形式来演示一下

在这里创建一个窗体项目,我们都知道窗体主线程堵塞时会导致窗体不能移动,所以能很好的看出效果

添加一个简单的方法.

async Task DemoAsync()
{
await Task.Run(() => { });
Thread.Sleep();
}

这个方法可以看到只有一个await 一个创建异步,然后线程睡眠3秒钟,

随后创建一个button按钮并添加一个事件,

 private  void button1_Click(object sender, EventArgs e)
{
DemoAsync();
MessageBox.Show("同步代码");
}

在这个事件中可以看到只调用了异步方法,调用异步方法时也并没有await,然后弹出一句话.

运行后会发现在点击button按钮时窗体不能被移动了,然后等待了3秒钟才弹出"同步代码"这句话,看到这里我们再看仔细想下上面的概念,好像明白了什么,下面我们改一下DemoAsync方法

  async Task DemoAsync()
{
await Task.Run(() => { Thread.Sleep(); });
Thread.Sleep();
}

可以看到只在子线程中添加了睡眠3秒的代码,然后我们再次运行就会神奇的发现,此时会先弹出"同步代码"这局话,然后等待3秒后窗体就不能被移动.看到这里我们就应该明白了为什么.

我们的第一次代码没有在子线程编写任何代码,所以await在执行第一次检查操作时就会立即返回,然后执行Thread.Sleep()代码阻塞主线程.

然而第二次代码在子线程中添加了睡眠3秒,所以在第一次检查操作师会发现并不会立即执行完毕,所以方法内以下代码也就是当前代码中的主线程睡眠3秒会作为await的后续代码(类似回调代码),跳出方法执行方法后面的代码,也就是弹出"同步代码"这句话,直到await等待子线程执行完毕后执行主线程睡眠那句代码,也就是主线程阻塞3秒钟.

2.ConfigureAwait方法

在Task里中有ConfigureAwait这么一个方法,这个方法是干什么的呢,我们先看下方法注释是怎么解释这个方法的:" 尝试将延续任务封送回原始上下文,则为 true;否则为 false。"  光看这段代码并看不出什么,然后我们再看这么一段话:"一个async方法是由多个同步执行的程序块组成.每个同步程序块之间由await语句分隔.用await语句等待一个任务完成.当该方法在await处暂停时,就可以捕捉上下文(context).如果当前SynchronizationContext不为空,这个上下文就是当前SynchronizationContext.如果为空,则这个上下文为当前TaskScheduler.该方法会在这个上下文中继续运行.一般来说,运行UI线程时采用UI上下文,处理ASP.NET请求时采用ASP.NET请求上下文,其它很多情况则采用线程池上下文。" 这句话已经基本讲明了其实后续代码会下上文中执行。这个上下文一般时UI上下文(运行在UI上)或请求上下文(ASP.NET) 这两个可以说时原始上下文,而其它情况采用线程池上下文,也就是开辟一个新线程。这么说也就是ConfigureAwait方法是将后续代码是送到原始上下文还是线程池上下文中

下面稍微修改下刚才的方法

async Task DemoAsync()
{
//将后续代码交给线程池执行
await Task.Run(() => { Thread.Sleep(); }).ConfigureAwait(false);
Thread.Sleep();
}

我们使用ConfigureAwait将后续代码交给线程池来执行,也就是下面的Thread.Sleep并不会阻塞窗体.运行后发现结果也是如我们所想,

最新文章

  1. Delphi Code Editor 之 编辑器选项
  2. jdbc执行Statement接口的步骤
  3. SQL数字转英文函数
  4. 将url的查询参数解析成字典对象
  5. UINavigationController的使用(多视图控制器)
  6. 使用行为树(Behavior Tree)实现网游奖励掉落系统
  7. R 环境内存限制的更改
  8. Android开发之定义app在手机的安装位置
  9. Python中的基本语句
  10. DXperience-12.1.5 官网下载+注册破解+帮助文档
  11. java处理图片时找到不sun.awt.X11GraphicsEnvironment问题
  12. Hello China操作系统STM32移植指南(一)
  13. 简单JSONP跨域请求
  14. 【原创】源码角度分析Android的消息机制系列(五)——Looper的工作原理
  15. Python的数据库操作
  16. php不用正则表达式实现身份证号验证详解
  17. for XML path 转义
  18. Berlekamp-Massey算法学习笔记
  19. D. Fun with Integers
  20. JUC-ReadWriteLock

热门文章

  1. Chris Richardson微服务翻译:微服务介绍
  2. 【转】jar包和war包的介绍和区别
  3. 让 MyBatis Generator 变的更简单
  4. JDK源码 - ArrayList
  5. typeof关键字的作用
  6. Linux批量处理文件脚本
  7. deeplearning.ai 构建机器学习项目 Week 1 机器学习策略 I 听课笔记
  8. 【易语言学习】Day1
  9. bzoj 3874: [Ahoi2014]宅男计划
  10. 教你如何解决Sublime Text 3使用中出现的中文乱码问题