目录
await 运算符暂停对其所属的 async 方法的求值,直到其操作数表示的异步操作完成。 异步操作完成后,await 运算符将返回操作的结果(如果有)。 当 await 运算符应用到表示已完成操作的操作数时,它将立即返回操作的结果,而不会暂停其所属的方法。 await 运算符不会阻止计算异步方法的线程。 当 await 运算符暂停其所属的异步方法时,控件将返回到方法的调用方。
下面就演示 await 运算符常用的一些用法。
新建一个基于 .Net6 的 Winform 项目,界面就两个按钮,如下:

代码
- namespace 异步编程
- {
- public partial class Form1 : Form
- {
- public Form1()
- {
- InitializeComponent();
- }
-
- private void button1_Click(object sender, EventArgs e)
- {
- IsTrue = false;
- AwaitEnd();
- }
-
- private void button2_Click(object sender, EventArgs e)
- {
- IsTrue = true;
- }
-
-
- private bool IsTrue = false;
- private Task<string> StartTimer()
- {
- var t = Task.Run(() =>
- {
- while (true)
- {
- if (IsTrue)
- return "555";
- Thread.Sleep(100);
- }
- });
- return t;
- }
-
- private async void AwaitEnd()
- {
- Console.WriteLine("开始执行,时间:" + DateTime.Now.ToString());
- var res = await StartTimer();
- Console.WriteLine("结束:" + res + " 时间:" + DateTime.Now.ToString());
- }
- }
- }
点击按钮1开始启动异步,点击按钮2,就返回结果,如果不点击按钮2,那么 while 循环就不会停止。
效果:

可以看到,点击了按钮1后,并不会让主线程卡死,窗体还是可以随意的拖动的,直到 StartTimer 方法将返回值返回回来后,才会继续执行后续的代码,这对一些需要阻塞线程,并获取另外的计算结果,然后才能继续计算的需求而言,有极大的帮助,比如读取数据库数据,如果网速比较慢,并且不会立刻就返回结果,用 await 运算符就可以在同一个方法里,等到获取到数据库返回结果后,再进行下一步运算,而不是从上到下,一下子就执行完了。
下面是以前我查询数据库写的代码,效果和上面演示中的 await 运算符是一样的,在下面的方法中,使用 Action 回调,代码都没写到一起,虽然逻辑一样,但用起来就不是那么的方便。
- ///
- /// 执行SQL语句,并获取值
- ///
- ///
- ///
- private static void ExecuteAndReturnValue(string sql, Action
callBack ) - {
- Func
Funcs = () => - {
- DataSet dataSet = MySqlHelper.GetDataSet(sql);
- if (dataSet == null || dataSet.Tables.Count == 0)
- return null;
- return dataSet.Tables[0];
- };
-
- //执行任务
- Task
printRes = Task.Run(Funcs); -
- //等待任务完成
- printRes.GetAwaiter().OnCompleted(() =>
- {
- if (callBack != null)
- callBack(printRes.Result);
- });
- }
当前的示例,只是执行单个任务,如果有多个任务,用下面的方法也是可以的,
- private Task DoSomethingAsync(int x)
- {
- return Task.Run(() =>
- {
- Thread.Sleep(1000);
- Console.WriteLine("值:" + x);
- });
- }
-
- public async Task RunAsync()
- {
- foreach (var x in new[] { 1, 2, 3 })
- {
- await DoSomethingAsync(x);
- }
- }
DoSomethingAsync 方法中,返回值可以从另一个数组中获取到 Task 并执行,我这里就不写那么仔细了,如果用面向过程的写法,就是这么写的:
-
- private async void Test()
- {
- await Task.Run(async () =>
- {
- await Task.Delay(4000);
- Trace.WriteLine("第1个线程执行");
- });
- await Task.Run(async () =>
- {
- await Task.Delay(3000);
- Trace.WriteLine("第2个线程执行");
- });
- await Task.Run(async () =>
- {
- await Task.Delay(2000);
- Trace.WriteLine("第3个线程执行");
- });
- }
在方法里加上了 async 关键字后,返回值就只能使用固定的几个了,不然会报错。
异步函数的返回类型只能为: void、Task、Task
Task
Task: 代表一个无返回值的操作。
void: 为了和传统的事件处理程序兼容而设计。
可以使用 await foreach 语句来使用异步数据流,即实现 IAsyncEnumerable
代码
- private async void Test()
- {
- IAsyncEnumerable<int> pullBasedAsyncSequence = ProduceAsyncSumSeqeunc(5);
- //开始另一项任务;用于使用异步数据序列!
- var consumingTask = Task.Run(() => ConsumeAsyncSumSeqeunc(pullBasedAsyncSequence));
-
- await Task.Delay(TimeSpan.FromSeconds(3));
- Console.WriteLine("搞一些其他事");
-
- //只是为了演示!等待任务完成!
- await consumingTask;
-
- Console.WriteLine("异步流演示完成!" );
- }
-
- private async Task ConsumeAsyncSumSeqeunc(IAsyncEnumerable<int> sequence)
- {
- Console.WriteLine("执行 ConsumeAsyncSumSeqeunc 方法");
-
- await foreach (var value in sequence)
- {
- Console.WriteLine($"value: {value}");
-
- await Task.Delay(TimeSpan.FromSeconds(1));
- };
- }
-
- private async IAsyncEnumerable<int> ProduceAsyncSumSeqeunc(int count)
- {
- Console.WriteLine("执行 ProduceAsyncSumSeqeunc 方法");
- int index = 0;
- for (int i = 0; i < count; i++)
- {
- await Task.Delay(TimeSpan.FromSeconds(0.5));
- yield return index += count;
- }
- }
调用 Test 方法后,即可打印

解释:创建将在时间延迟后完成的任务。命名空间: System.Threading.Tasks
在上面的演示中用到了多次,例:
await Task.Delay(TimeSpan.FromSeconds(0.5));
在 Delay 方法中,可以用 TimeSpan 中的时、分、秒 表示
参考:
| Delay(Int32) | 创建一个在指定的毫秒数后完成的任务。 |
| Delay(TimeSpan) | 创建一个在指定的时间间隔后完成的任务。 |
| Delay(Int32, CancellationToken) | 创建一个在指定的毫秒数后完成的可取消任务。 |
| Delay(TimeSpan, CancellationToken) | 创建一个在指定的时间间隔后完成的可取消任务。 |
如果这个帖子对你有所帮助,欢迎 关注 、点赞 、留言
end