当前位置: 首页 > news >正文

异步编程 await 和 async

文章目录

  • 前言
  • await 和 async
    • (1)Thread.sleep() 阻塞当前线程
    • (2)没有await 的 Task.Delay()
    • (3)有 await 的 Task.Delay()


前言

转载: 方程式sunny

视频教程: 跟着sunny老师学C#

源码: gitee仓库

2025年10月25日 18点52分 ---吃薯片看 T1(Faker竟然还在) 保送8强,顺便记录一下异步编程


await 和 async


  • 异步编程是指程序不需要等待任务的完成,而是可以继续执行其他操作。这样做的好处是,避免程序在处理耗时操作(如文件读取、网络请求)时界面被冻结或无响应。
  • 当然了,上面这个定义说的根本不是人话,比如任务是什么?怎么继续执行其他操作?其他操作是什么?界面被冻结和无响应又是什么?这些都没有解释清楚嘛。先声明一点,我只讲最基本的原理,为什么只讲最基本的呢?原因是最本质的我也没搞清楚,哈哈,大家凑合看吧。
  • 我们平常所说的异步编程,其实就是定义异步方法或者使用异步方法,定义和使用,对,这就是异步编程的全部。

我们先来看怎么看怎么定义一个普通方法:

public void Method()
{Console.WriteLine("这是一个普通方法");
}

好了,上面我们定义了一个普通的方法 Method,正常来说,一个普通方法也能用了呀,为什么还要什么异步方法呢?我们换一个例子。

public void Method()
{Console.WriteLine("这是一个普通方法");Thread.Sleep(5000); //模拟耗时操作
}

上面这个例子模拟了一个耗时操作,当前线程休眠5秒,这会造成什么后果呢?休眠了会阻塞当前线程,可能在控制台上不明显,我们换一个wpf或者winformUI界面。如果你此时在一个UI界面上点击一个按钮,那么界面就会卡主,得等到5秒的休眠结束,点击按钮的事件才会响应。


  • 遇到问题了,那就解决吧!怎么解决呢?有的小伙伴说用多线程技术重新开一个线程,的确可以,但是多线程参数传递、断点调试、全局变量控制等,会出现一系列的问题,使用起来也会很复杂。那有没有其他方法来解决这个问题呢?
  • 有,这就是异步编程,异步编程的原理很复杂,但作为程序员,不需要知道这些复杂的原理,我们只要会用就行了。于是呢,微软就发明了 async
    await 两个关键字,有了这两个关键字,好办了,编写异步方法就像编写同步方法一样简单。

由于上面的例子是为了我引出我讨论的话题,用的是一些零碎的代码片段,接下来呢我会用一个完整的代码来进一步解释,选的框架C#wpf,程序运行之后就如下图所示,当点击按钮1时,它下面的文本框文字会发生变化,当点击按钮2时,与它对应的文本框也会发生变化。前端代码和后端代码我都贴上了。


在这里插入图片描述

前端代码:

<Window x:Class="WpfTest.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:local="clr-namespace:WpfTest"mc:Ignorable="d"Title="MainWindow" Height="450" Width="800"><Grid><Grid.RowDefinitions><RowDefinition Height="88*"/><RowDefinition Height="347*"/></Grid.RowDefinitions><Grid.ColumnDefinitions><ColumnDefinition/><ColumnDefinition/></Grid.ColumnDefinitions><Button Content="按钮1" Click="Button1_Click"/><Button Content="按钮2" Grid.Column="1" Click="Button2_Click"/><TextBox Grid.Row="1" Name="tbx1" Text="按钮1对应的文本"/><TextBox Grid.Row="1" Name="tbx2" Grid.Column="1" Text="按钮2对应的文本"/></Grid>
</Window>

后端代码:

using System.Windows;namespace WpfTest
{public partial class MainWindow : Window{int i = 0;int j = 0;public MainWindow(){InitializeComponent();}private async void Button1_Click(object sender, RoutedEventArgs e){Thread.Sleep(5000);//Task.Delay(5000);//await Task.Delay(3 * 1000);
​tbx1.Text = "修改了文本框1的内容\n";tbx1.AppendText(j++.ToString());}private void Button2_Click(object sender, RoutedEventArgs e){tbx2.Text = "修改了文本框2的内容\n";tbx2.AppendText(i++.ToString());} }
}

(1)Thread.sleep() 阻塞当前线程


  • 我们运行上面的代码,但我们点击按钮1时,文本框1的文字不会马上变化,5秒后,文本框1的文字发生变化,因为我们Thread.Sleep(5000)这行代码,让UI线程休眠了5秒。
  • 我们再换一个操作,点击了按钮1后,我们马上点击按钮2,我们发现不仅文本框1的文字没有马上发生改变,文本框2的文字也没有马上改变,5秒之后,我们发现文本框1文本框2的文字都发生变化了。
  • 还是一样的原因,因为这个软件只有一个UI线程(主线程),当我们先点击按钮1时,UI线程阻塞了,点击按钮2文本框2文字也就不会马上发生变化,5秒后文本框1文本框2的文字都发生变比了。
  • 通过以上两个小实验,我们得出结论:Thread.sleep()会阻塞UI线程。

(2)没有await 的 Task.Delay()


  • 我们接着来做小实验,注释Thread.Sleep(5000);同时取消注释Task.Delay(5000);这行代码。运行程序,我们先点击按钮1,我们发现,文本框1的文字马上发生变化,并不是5秒后文本框才发生变化,这是为什么呢?
  • 原来Task.Delay()是一个异步方法,它不会阻塞UI线程。事情到这里还没完,我的需求是不阻塞UI线程,同时还要延迟5秒,不阻塞线程的确满足了,延迟5秒显然不满足。
  • 原来在调用异步方法时,我们还需要使用在调用异步方法前面使用关键字await,例如:await Task.Delay(3 * 1000)Task.Delay()是一个异步方法,调用该异步方法时前面要使用await关键字,所以你看到 Task.Delay(3 * 1000)前面有一个关键字await
  • 但在这个例子中,我们并没有使用await,并没有等待异步方法的结果,系统就会直接执行异步方法后面的代码,这里后面的方法就是 tbx1.Text = "修改了文本框1的内容\n"; tbx1.AppendText(j++.ToString()); 所以文本框1的文字马上发生了变化。
  • 这个小实验还没完,我们重新运行程序,当点击按钮1时,我们又马上点击按钮2。我们可以看到,文本框1的内容马上发生变化,文本框2的内容也是立刻变化。
  • 我们也得出了一个小结论:调用异步时不使用await的确不会阻塞UI线程,但是延迟的功能没有实现。

(3)有 await 的 Task.Delay()


  • 最后一个小实验,我们在调用异步方法时使用关键字await,注释Task.Delay(5000);取消注释 //await Task.Delay(3 * 1000); 当我们运行程序时,点击按钮1,我们发现文本框1的内容没有立刻发生变化,5秒之后,文本框1的内容才发生变化,延迟功能的确是实现了,但会不会阻塞,我们还得继续实验。
  • 重新运行程序,先点击按钮1,然后立刻点击按钮2。我们发现文本框2的内容马上发生变化,但是文本框1的内容是5秒后才发生变化的,并且我们发现点击按钮1后,马上疯狂点击按钮2,整个UI界面并没有卡顿, 延迟功能实现了,不阻塞UI界面也实现了,妙呀。
  • 事情到这里似乎所有的问题都得到了圆满的解决,但我们还没有讨论如何定义一个异步方法。

我们来写一个异步方法:

public async Task Myfun()
{Console.WriteLine("hello, Mr sunny!");
}
  • 虽然这个例子使用了async关键字,但实际上它并没有实现异步操作,因为没有任何耗时操作。Task是返回类型,与普通方法返回类型voidintfloat等类似,Task是一个泛型类型。例如,普通方法返回void时,异步方法返回Task;普通方法返回int时,异步返回类型为Task<int>

  • 为了使这个方法成为真正的异步方法,我们需要添加await

public async Task Myfun()
{await Task.Delay(3 * 1000);Console.WriteLine("hello, Mr sunny!");    
}
  • 现在,这个方法可以异步执行。当我们在UI线程调用Myfun时,即使内部有3秒的延迟,UI线程仍然可以继续处理其他操作,不会阻塞界面。
  • 那么,为什么不阻塞UI线程呢?当UI线程调用Myfun并执行到await Task.Delay(3 * 1000);时,系统发现有await,便将控制权返回给调用者(即UI线程)。此时,Console.WriteLine("hello, Mr sunny!");这行代码不会立即执行,因为它在await之后。
  • 虽然await后面的代码在await执行时被“暂停”,但并不是永远不执行。await Task.Delay(3 * 1000);会创建一个3秒的定时器,这个定时器由系统的线程池管理。定时到达后,系统会通过事件机制将控制权返回给之前的上下文,通常是UI线程。
  • 具体说怎么找到之前没有运行完的代码,别问我,我也没有搞清楚,反正能找到没有执行完的代码。
  • 需要强调的是,3秒的延迟是由系统管理的,而不是C#代码直接控制的。如果主线程忙碌,系统会从线程池中选择一个空闲线程来处理这些后续的代码执行。

http://www.dtcms.com/a/528413.html

相关文章:

  • Flask 学习路线图
  • 大数据统计网站南宁7天优化网络科技公司
  • ajax网站开发技术网店设计素材
  • GitHub 热榜项目 - 日榜(2025-10-25)
  • 【bug解决】[string “tolua.lua“]:1: ‘=‘ expected
  • Windows 10/11用户报告开始菜单和搜索栏故障
  • 仓颉语言核心技术解析:如何开发高性能服务端应用
  • Redis分布式锁演进全解析
  • 实时性要求高的场景中实现增量式遗传算法更新
  • 广告传媒建设网站网站策划建设阶段的推广
  • 从零开始:C++ TCP 服务器实战教程
  • csv文件用Excel打开后出现乱码的问题及其解决方法
  • 【Swift】LeetCode 56. 合并区间
  • 上海免费建站模板iis添加网站 别名
  • Linux: 网络: SIPp导致的网络风暴
  • 从0开始学java--day6.5
  • 厦门网站制作公司域名注册需要多少钱
  • AN-25101701 UG56网关与WS101传感器连接TKE132 LoRaWAN服务器指导说明书
  • 如何做网站制作杭州高端网站建设到蓝韵网络
  • Z.EntityFramework.Extensions.EFCore 批量更新(BulkUpdate)指定字段
  • MLLM-LLaVA-FL: Multimodal Large Language Model Assisted FederatedLearning
  • 欧美教育网站模板中国建设银行信用卡网站首页
  • 【同步 / 异步 日志系统】--- 全局接口 性能测试
  • GitHub等平台形成的开源文化正在重也有人
  • 03_Pushgateway使用Prometheus的服务发现机制
  • Speckit和Claude 的初体验
  • 当夸克遇上大模型:中国 AI 产品的“第二阶段”来临了
  • AI大模型弹性伸缩实战:自动扩缩容+模型轻量化+Serverless三大技术方案详解
  • 网站怎么做的qq邮件订阅页面设计存在的问题
  • CMP(类ClouderaCDP7.3(404次编译) )完全支持华为鲲鹏Aarch64(ARM),粉丝数超过200就开源下载