浅谈Await

简介: 1.Await为什么不会导致堵塞      我们都知道Await关键字是.Net FrameWork4.5引入的特性。await使得我们使用异步更加时特别便捷,并且还不会导致线程堵塞。我们在使用时也就莫名其妙的使用。

   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(3000);
        }

这个方法可以看到只有一个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(3000); });
            Thread.Sleep(3000);
        }

可以看到只在子线程中添加了睡眠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(3000); }).ConfigureAwait(false);
            Thread.Sleep(3000);
        }

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

相关文章
|
Linux
七个办法只有一个有效:200 PORT command successful. Consider using PASV.425 Failed to establish connection.
七个办法只有一个有效:200 PORT command successful. Consider using PASV.425 Failed to establish connection.
4231 0
七个办法只有一个有效:200 PORT command successful. Consider using PASV.425 Failed to establish connection.
|
人工智能 多模数据库 Cloud Native
揽获多项殊荣,阿里云瑶池数据库亮相2024可信数据库发展大会
在2024可信数据库发展大会上,阿里云被选为中国信通院数据库金融工作组共建单位。同时,阿里云Lindorm成为首批通过中国信通院多模数据库产品测试的产品,展示出在多模数据处理能力上的领先性。
|
12月前
|
缓存 负载均衡 应用服务中间件
Nginx 实现一个端口代理多个前后端服务
【10月更文挑战第19天】Nginx 的强大功能不仅限于此,它还可以与其他技术和工具相结合,为我们的应用提供更强大的支持和保障。在不断发展的互联网时代,掌握 Nginx 的使用技巧将为我们的工作和生活带来更多的便利和效益。
|
消息中间件 关系型数据库 Kafka
深入理解数仓开发(二)数据技术篇之数据同步
深入理解数仓开发(二)数据技术篇之数据同步
|
10月前
|
Web App开发 网络协议 安全
网络编程懒人入门(十六):手把手教你使用网络编程抓包神器Wireshark
Wireshark是一款开源和跨平台的抓包工具。它通过调用操作系统底层的API,直接捕获网卡上的数据包,因此捕获的数据包详细、功能强大。但Wireshark本身稍显复杂,本文将以用抓包实例,手把手带你一步步用好Wireshark,并真正理解抓到的数据包的各项含义。
1679 2
|
人工智能 安全 数据挖掘
AI在灾害预警与管理中的应用:提升应急响应能力
【9月更文挑战第23天】AI在灾害预警与管理中的应用正在逐步改变我们对灾害的应对方式。通过实时监测与数据分析、精准预测与风险评估、快速响应与决策支持、智能调度与资源优化以及灾后评估与恢复重建等多种手段,AI正逐步提升我们的应急响应能力,为保障人民生命财产安全提供有力支持。未来,随着AI技术的不断发展和完善,我们有理由相信,AI将在灾害预警与管理中发挥更加重要的作用,为人类社会的可持续发展贡献更多力量。
|
缓存 安全 算法
Spring Security OAuth 2.0 资源服务器— JWT
Spring Security OAuth 2.0 资源服务器— JWT
1046 1
|
12月前
|
SQL Oracle 关系型数据库
SQL语句中的引号使用技巧:正确处理字符串与标识符
在编写SQL语句时,引号的使用是一个基础且重要的环节
1549 0
|
开发框架 前端开发 JavaScript
使用Winform开发自定义用户控件,以及实现相关自定义事件的处理
使用Winform开发自定义用户控件,以及实现相关自定义事件的处理
|
运维 Rust 监控
Linux高效运维必备:fd命令深度解析,文件描述符管理从此得心应手!
【8月更文挑战第23天】本文介绍了一款名为fd的命令行工具,该工具基于Rust语言开发,旨在以更直观的语法和更快的速度替代传统的`find`命令。通过本文,您可以了解到如何安装fd以及一些基本用法示例,比如使用正则表达式匹配文件名、排除特定目录等。此外,文章还展示了如何结合`ps`和`lsof`命令来查找特定文件并显示其文件描述符,从而帮助您更好地管理和监控Linux系统中的文件与进程。
660 0