C# async / await 用法

news2024/11/23 11:05:26

目录

一、简介

二、异步等待返回结果

三、异步方法返回类型

四、await foreach

五、Task.Delay

结束


一、简介

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 回调,代码都没写到一起,虽然逻辑一样,但用起来就不是那么的方便。

/// <summary>
/// 执行SQL语句,并获取值
/// </summary>
/// <param name="sql"></param>
/// <param name="callBack"></param>
private static void ExecuteAndReturnValue(string sql, Action<DataTable?> callBack)
{
    Func<DataTable?> Funcs = () =>
    {
        DataSet dataSet = MySqlHelper.GetDataSet(sql);
        if (dataSet == null || dataSet.Tables.Count == 0)
            return null;
        return dataSet.Tables[0];
    };

    //执行任务
    Task<DataTable?> 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<TResult>、ValueTask 或 ValueTask<TResult>

Task<TResult>: 代表一个返回值T类型的操作。

Task: 代表一个无返回值的操作。

void: 为了和传统的事件处理程序兼容而设计。

四、await foreach

可以使用 await foreach 语句来使用异步数据流,即实现 IAsyncEnumerable<T> 接口的集合类型。 异步检索下一个元素时,可能会挂起循环的每次迭代。

代码

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 方法后,即可打印

五、Task.Delay

解释:创建将在时间延迟后完成的任务。命名空间: System.Threading.Tasks

在上面的演示中用到了多次,例:

await Task.Delay(TimeSpan.FromSeconds(0.5));

在 Delay 方法中,可以用 TimeSpan 中的时、分、秒 表示

参考:

Delay(Int32)

创建一个在指定的毫秒数后完成的任务。

Delay(TimeSpan)

创建一个在指定的时间间隔后完成的任务。

Delay(Int32, CancellationToken)

创建一个在指定的毫秒数后完成的可取消任务。

Delay(TimeSpan, CancellationToken)

创建一个在指定的时间间隔后完成的可取消任务。

结束

如果这个帖子对你有所帮助,欢迎 关注 、点赞 、留言

end

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/14500.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

遨博机械臂——末端工具ROS驱动

文章目录知识目标1. 机械臂末端工具&#xff08;EOAT&#xff09;2. 电动夹爪3. 气动吸盘参考知识目标 学习机械臂常用末端工具构成&#xff1b;学习aubo机械臂安装电动夹爪及启动吸盘的方法&#xff1b;学习电动夹爪及气动吸盘ROS驱动的使用方法。 1. 机械臂末端工具&#x…

【附源码】计算机毕业设计JAVA校园社团管理平台

【附源码】计算机毕业设计JAVA校园社团管理平台 目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; JAVA my…

“3%”与“低个位数”,暴雪为什么要跟网易玩数字游戏?

北京时间11月17日上午&#xff0c;暴雪娱乐发布声明称&#xff0c;由于同网易的现有授权协议将在2023年1月23日到期&#xff0c;将暂停在中国大陆的大部分暴雪游戏服务&#xff0c;包括《魔兽世界》《炉石传说》《守望先锋》《星际争霸》《魔兽争霸Ⅲ&#xff1a;重置版》《暗黑…

[iOS]砸壳

进行砸壳&#xff0c;需要有台越狱手机。如何越狱&#xff0c;参考前一篇“[iOS]手机越狱”。 没有越狱设备的话&#xff0c;可以去某宝寻求帮助&#xff0c;有帮忙砸壳。 1.添加源 打开Cydia软件&#xff0c;软件源中选择编辑&#xff0c;添加源https://cydia.iphonecake.co…

文件预览服务器kkfileview安装部署(linux 版)

1、安装 LiberOffice 安装包 命令下载 wget https://kkfileview.keking.cn/LibreOffice_7.1.4_Linux_x86-64_rpm.tar.gz tar -zxvf LibreOffice_7.1.4_Linux_x86-64_rpm.tar.gz cd LibreOffice_7.1.4.2_Linux_x86-64_rpm/RPMS yum install -y *.rpm 2、验证office是否安装成功…

【MySQL】MVCC详解与MVCC实现原理(MySQL专栏启动)

&#x1f4eb;作者简介&#xff1a;小明java问道之路&#xff0c;专注于研究 Java/ Liunx内核/ C及汇编/计算机底层原理/源码&#xff0c;就职于大型金融公司后端高级工程师&#xff0c;擅长交易领域的高安全/可用/并发/性能的架构设计与演进、系统优化与稳定性建设。 &#x1…

Vue2 Element | 一文带你快速搭建网页界面UI

&#x1f451; 博主简介&#xff1a;    &#x1f947; Java领域新星创作者    &#x1f947; 阿里云开发者社区专家博主、星级博主、技术博主 &#x1f91d; 交流社区&#xff1a;BoBooY&#xff08;优质编程学习笔记社区&#xff09; 前言&#xff1a;在学习本篇文章内容…

Cholesterol胆固醇丨艾美捷Cholesterol胆固醇化学性质

胆固醇是由甾体部分和一条长的侧链组成。人体中胆固醇的总量大约占体重的0.2%&#xff0c;每100克组织中&#xff0c;骨质约含10毫克&#xff0c;骨骼肌约含100毫克&#xff0c;内脏多在150~250毫克之间&#xff0c;肝脏和皮肤含量稍高&#xff0c;约为300毫克。脑和神经组织中…

【JVM学习笔记】JVM内存区域定义与内存结构

目录定义和说明JVM内存区域的定义内存区域说明堆说明非堆-方法区说明堆栈的区别HotSpot虚拟机JVM线程独占内存程序计数器&#xff1a;Program Counter RegisterJava虚拟机栈&#xff1a;Java Virtual Machine Stack本地方法栈&#xff1a;Native Method StackJVM共享内存Java堆…

Arduino与Proteus仿真实例-密码门禁控制仿真

密码门禁控制仿真 1、应用介绍 本文将演示如何实现密码门禁控制逻辑仿真。 此次仿真主要涉及如下内容: 密码输入、更新、验证门禁控制逻辑此次仿真将使用继电器和直流电机作为电子门禁元件仿真器件。 在前面的文章中,对密码输入、更新、验证、储存,做了详细的仿真,请参…

API:低代码平台的秘诀

应用编程接口 (API) 是应用程序以可编程格式访问其关键能力和功能的一种方式&#xff0c;从而其他应用程序可以利用它们。API 本质上支持应用程序之间的无缝数据流&#xff0c;使开发人员能够在应用程序中添加更多功能&#xff0c;而无需依赖大量编码。 举一个简单的例子。 您…

实战!接口优化的18种方案

前言 大家好&#xff0c;我是捡田螺的小男孩。 之前工作中&#xff0c;遇到一个504超时问题。原因是因为接口耗时过长&#xff0c;超过nginx配置的10秒。然后 真枪实弹搞了一次接口性能优化&#xff0c;最后接口从11.3s降为170ms。本文将跟小伙伴们分享接口优化的一些通用方案…

maven如何手动添加jar包到本地仓库

1 下载需要添加的jar包 可以在maven库中查找下载&#xff0c;也可以在对应官网下载 maven库网址 2 第二步&#xff1a;将下载的jar包放到指定位置&#xff08;位置自己指定&#xff09; 3 第三步&#xff1a;配置本地maven库 &#xff08;1&#xff09;首先检查本地maven库…

结构优化软件SolidThinking Inspire的自学攻略

作者&#xff1a;孙一凡&#xff0c;仿真秀专栏作者 2004年上大学那会&#xff0c;ANSYS软件推广应用还没现在这么广泛&#xff0c;有个老师接项目就是用ANSYS计算&#xff0c;觉得很是高大上&#xff01;ABAQUS还是一个小众软件&#xff0c;甚至一本参考资料书籍都买不到。短…

前端一面经典react面试题(边面边更)

react 的虚拟dom是怎么实现的 首先说说为什么要使用Virturl DOM&#xff0c;因为操作真实DOM的耗费的性能代价太高&#xff0c;所以react内部使用js实现了一套dom结构&#xff0c;在每次操作在和真实dom之前&#xff0c;使用实现好的diff算法&#xff0c;对虚拟dom进行比较&…

SVM与基于马氏距离的径向基函数(MDRBF)核结合组合(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜…

Scala 数组

Scala 语言中提供的数组是用来存储固定大小的同类型元素&#xff0c;数组对于每一门编辑应语言来说都是重要的数据结构之一。 声明数组变量并不是声明 number0、number1、...、number99 一个个单独的变量&#xff0c;而是声明一个就像 numbers 这样的变量&#xff0c;然后使用…

【设计模式】建造者模式

1. 概述 建造者模式将复杂产品的创建步骤分解在不同的方法中&#xff0c;使得创建过程更加清晰&#xff0c;从而更精确控制复杂对象的生产过程&#xff1b; 通过隔离复杂对象的构建与使用&#xff0c;也就是将产品的创建与产品本身分离开来&#xff0c;使得同样的构建过程可以…

Python编程从入门到实践 第七章:用户输入和while循环 练习答案记录

Python编程从入门到实践 第七章&#xff1a;用户输入和while循环 练习答案记录 练习题导航Python编程从入门到实践 第七章&#xff1a;用户输入和while循环 练习答案记录7.1 函数input()的工作原理7.1.1 编写清晰的程序7.1.2 使用int()来获取数值输入7.1.3 求模运算符练习7-1 汽…

SpringMVC学习篇(十)

springmvc拦截器之重复提交 1 出现原因 在新增和修改界面点击提交后(转发的方式跳转) 再次刷新页面,如果不做处理的话,会造成重复提交, 从而使得新增商品多次或者更改商品多次2 解决方案 2.1 准备工作 导入servlet-api依赖和spring-webmvc依赖 <dependency><group…