首页 > 其他分享 >C# await 高级用法

C# await 高级用法

时间:2024-10-11 10:49:30浏览次数:11  
标签:Task 代码 await 高级 用法 Write 线程 static

本文告诉大家 await 的高级用法,包括底层原理。

昨天看到太子写了一段代码,我开始觉得他修改了编译器,要不然下面的代码怎么可以编译通过

await "林德熙逗比";

需要知道,基本可以添加 await 都是可以等待的类型,如 Task 。如果一个类需要可以被等待,那么这个类必须满足以下条件

  • 类里有一个 GetAwaiter 函数
  • GetAwaiter 有返回值,返回值需要继承 INotifyCompletion 并且有 bool IsCompleted { get; },GetResult(),void OnCompleted(Action continuation) 定义

参见:如何实现一个可以用 await 异步等待的 Awaiter - walterlv

但是上面的代码使用的是一个字符串,什么时候可以修改继承字符串?

先让我来说下 await 原理,因为知道了原理,上面的代码实现很简单。看完了本文,你就会知道如何让几乎所有类型包括 int 、string 、自定义的类都支持 await 。

如果真的不想看原理,那么请直接调到文章的最后,看到最后很快就知道如何做。

原理

在 .net 4.5 之后,框架默认提供 async 和 await 的语法糖,这时千万不要认为进入 await 就会进入一个新的线程,实际上不一定会进入一个新的线程才会调用 await 。

那么 await 的语法糖写的是什么?实际上就是以前的 Begin xx 和 End xx 的语法糖。

古时候的写法:

foo.Beginxx();

foo.Endxx(传入委托);

这样大家就无法在一个流程写完,需要分为两个东西,而在 Continus with 下,就需要传入委托。如果委托里又使用了异步,那么又需要传入委托

task.ContinueWith(_ =>
            {
                Task t1 = new Task(() => { });
                t1.ContinueWith((t2) =>
                {
                    //可以看到如果进入很多的委托
                });
            });

所以这时就使用了 await ,可以让大家按照顺序写。

await task;
Task t1 = new Task(() => { });
await t1;
//可以看到这时不需要进入委托

实际上 await 是在编译时支持的,请看进阶篇:以IL为剑,直指async/await - 布鲁克石

而且千万不要认为 await 一定会进入一个新的线程,实际上他只是把需要写在多处的代码,可以按照流写下载,和写同步代码一样。如果感兴趣 await 不一定会进入一个新的线程请看 There Is No Thread

使用

因为 await 需要找到一个 GetAwaiter 函数,这个函数即使是扩展方法也可以,所以其实上面的代码是这样写的

public static class KvpbamjhKsvm
    {
        public static HeabdsdnbKevx GetAwaiter(this string str)
        {
            return new HeabdsdnbKevx();
        }
    }

    public class HeabdsdnbKevx : INotifyCompletion
    {
        public bool IsCompleted { get; }

        public void GetResult()
        {
        }

        /// <inheritdoc />
        public void OnCompleted(Action continuation)
        {
        }
    }

HeabdsdnbKevx 就是一个可以等待的类型

现在就可以写出下面的代码

private static void Main(string[] args)
        {
            DdngSiwchjux();
        }

        private static async void DdngSiwchjux()
        {
            await "林德熙逗比";
        }

当然,上面的这个代码可以运行,不过不会返回什么。下面让我加上一句代码。

private static void Main(string[] args)
        {
            DdngSiwchjux();
        }

        private static async void DdngSiwchjux()
        {
            await "林德熙逗比";
            Console.WriteLine("cs");
        }

这时可以看到,Console.WriteLine("cs");不会运行,因为这时如果在 OnCompleted 函数打断点就可以看到,执行await "林德熙逗比"之后就进入OnCompleted 函数。从上面的原理可以知道,这个函数传入的参数就是两个awaitawait和函数结束之间的代码。如果需要让Console.WriteLine("cs");运行,那么只需要在OnCompleted运行参数

C# await 高级用法_ci

public void OnCompleted(Action continuation)
        {
            continuation();
        }

C# await 高级用法_扩展方法_02

但是作为一个挖坑专业的大神,怎么可以就扩展 string ,下面我把 int 进行扩展

public static class KvpbamjhKsvm
    {
        public static HeabdsdnbKevx GetAwaiter(this int dxpbnzSce)
        {
            return new HeabdsdnbKevx();
        }
    }

随意写一个值,然后进行等待

C# await 高级用法_扩展方法_03

现在我准备在 object 加一个扩展方法,所有类型都可以等待,然后把这个扩展方法的 namespace 写为 System ,这样大家就不知道这个是我写的,过了一年我就告诉大家这是 C# 的特性,所有的类都可以等待。但是这个特性需要开光才可以使用,你们直接建的项目没有开光所以没法使用这个特性。

等待和不等待的区别

虽然很多时候从原理上看,等待和不等待只是调用时机的问题。但是依旧遇到一些小伙伴一直以为全部的异步方法都需要await,看到我写了没有直接await的代码觉得很诡异,所以我在这里做个实验给大家看。

下面的代码是最常见的代码,在 async Task 的方法使用 await ,这样就会等待这个方法完成,代码就和同步代码一样。

await GagarLerecel();
private static async Task GagarLerecel()

例如我这样写

await GagarLerecel();

        private static async Task GagarLerecel()
        {
            Write("GagarLerecel 开始");
            await Task.Delay(100);
            Write("GagarLerecel 完成");
        }

输出就是按照顺序输出

GagarLerecel 开始
GagarLerecel 完成

如果我修改一下代码,创建一个新的函数 CoujafuDarso 里面的代码和上面函数相同

private static async Task CoujafuDarso()
        {
            Write("CoujafuDarso开始");
            await Task.Delay(100);
            Write("CoujafuDarso结束");
        }

但是不在调用 CoujafuDarso 使用 await ,而是使用一个变量

var aa = CoujafuDarso();
            Write("其他代码");
            await aa;

就是这样的代码,我的小伙伴说,这样写不清真,实际上这样写也是清真的代码。在调用 CoujafuDarso 会在代码到第一个 await 函数就返回,于是先执行了CoujafuDarso开始,然后函数返回,执行Write("其他代码"),在最后await aa才等待函数把所有代码执行完成。所以可以看到下面输出

CoujafuDarso开始
其他代码
CoujafuDarso结束

但是不加 await 的呢?也就是函数一直都没有等待,我再写一个函数BotujawWorpay

private static async Task BotujawWorpay()
        {
            Write("BotujawWorpay开始");
            await Task.Delay(100);
            Write("BotujawWorpay结束");
        }

调用的时候没有等待

BotujawWorpay();
            Write("CesearJemmeme");

这时会在输出CesearJemmeme之后,某个时间继续执行函数

BotujawWorpay开始
CesearJemmeme
BotujawWorpay结束

这样和使用 void 函数有什么区别?

在执行的函数遇到第一个 await 就会返回,这样就可以继续执行函数下面的代码

C# await 高级用法_ci_04

输出下面代码

德熙逗比代码
BarpooseewhowGelpousacall 代码1 线程1
德熙逗比状态开始
BarpooseewhowGelpousacall 代码2 线程5
BarpooseewhowGelpousacall 代码3 线程4
BarpooseewhowGelpousacall 完成 线程5

多线程

不是所有的 await 都会开多线程,如下面的代码

static void Main(string[] args)
        {
            Write("开始");
            Write("线程" + Thread.CurrentThread.ManagedThreadId);

            CeaXisci();
            Task.Run(async () =>
            {
                await Task.Delay(1000);
                MouvaypuNasjo();
            });
            while (true)
            {
                Console.Read();
            }
        }

        private static async Task BarpooseewhowGelpousacall()
        {
            Write("BarpooseewhowGelpousacall 代码1 线程" + Thread.CurrentThread.ManagedThreadId);
            await Task.Delay(10);
            Write("BarpooseewhowGelpousacall 代码2 线程" + Thread.CurrentThread.ManagedThreadId);
            await Task.Delay(10);
            Write("BarpooseewhowGelpousacall 完成 线程" + Thread.CurrentThread.ManagedThreadId);
        }

也就是在没有Task.Delay分开的代码,只要使用了 await 那么就可以在同个线程运行,请看输出。在最后的BarpooseewhowGelpousacall 完成和这个函数后面的代码都在同一个线程运行,而上面的代码,可能是在同个线程,也可能在不同的线程

开始
线程1
CeaXisci 开始 线程1
BarpooseewhowGelpousacall 代码1 线程1
BarpooseewhowGelpousacall 代码2 线程5
BarpooseewhowGelpousacall 完成 线程4
CeaXisci 开始 完成4




标签:Task,代码,await,高级,用法,Write,线程,static
From: https://blog.51cto.com/u_11283245/12216159

相关文章

  • Oracle中alter table的常用用法
    首发微信公众号:SQL数据库运维原文链接:https://mp.weixin.qq.com/s?__biz=MzI1NTQyNzg3MQ==&mid=2247486440&idx=1&sn=b8a50ce5e993b4ab196ddda705077d95&chksm=ea375f98dd40d68ea079d90ac6084078e8ec9e1a4b1f4cc266fb97976dc2c72f452a61f55850&token=1175589249&la......
  • Stata教程:高级虚拟变量处理和标签添加
    Stata教程:高级虚拟变量处理和标签添加小菲stata,全网同名Hello,大家好,接粉丝提问,这期给大家分享虚拟变量处理和标签添加,在这个教程中,我们将使用Stata的"nlsw88"数据集(这是一个关于1988年美国女性劳动力的数据集)来演示如何创建更复杂的虚拟变量,添加详细的标签,并进行基本的统......
  • MySQL调优指南及高级SQL技巧
    知识背景:相信大家在面试的时候都会遇到关于mysql调优的问题,这已经是必问的一环节了,特此在这边简单记录一些关于mysql调优的知识点,希望对大家有帮助!总体介绍:MySQL调优主要分为三个步骤:监控报警、 排查慢SQL、MySQL调优。监控报警:使用工具(如Prometheus+Grafana)监控MySQL,发......
  • Curl一些基础用法
    这几天遇到一个很好用的工具,curl以下是curl的一些基础用法。url是一个非常强大的命令行工具,用于传输数据,支持多种协议,如HTTP、HTTPS、FTP等。以下是一些基本的curl语法和常用命令:基本语法curl[选项][URL...]常用选项-v,--verbose:详细模式,显示通信的整个过程。-s,--s......
  • ‌ComfyUI 高级实战:实现华为手机的AI消除功能
    大家好,我是每天分享AI应用的萤火君!不知道大家是否还记得华为Pura70的「AI消除」事件,当时使用华为Pura70系列手机的智能消除功能时,该功能可以被用来消除照片中女性胸口处的衣物,这一功能曾引发广泛的关注和伦理担忧‌,后来华为迅速修复了这一问题。这篇文章就来介绍如何通......
  • JS高级-ES6之类与继承实现
    在本章节中,我们会通过class类的继承做法extends来实现继承,相对于过往在原型链章节所学的各种继承方式,便利程度有着飞跃性的提升类继承的关键因素super关键词是如何使用的?Babel工具是如何将ES6的类继承转为ES5的旧写法?阅读这类转化过的源码,我们需要注意哪些技巧?在该篇章中,都......
  • JS高级-ES6之模板字符串与剩余参数
    在本章节中,我们学习新的字符串拼接方式:标签模板字符串,动态效果与自由使用程度得到进一步提升函数的默认参数更好的解决方案,以及结合解构的进阶使用方式剩余参数的进一步说明,箭头函数的补充,以及展开语法对数据的处理细节是怎么样的,深拷贝还是浅拷贝,都会得到说明一、字符......
  • 初始vector——数组的高级产物
    前言:C++标准模板库(STL)是现代C++编程的基石,其中的容器、算法和迭代器为开发者提供了高效、灵活的数据处理工具。vector作为STL中最常用的顺序容器,不仅支持动态数组的功能,还通过自动内存管理和丰富的操作接口,极大简化了数据操作的复杂性。无论是在日常开发还是算法竞赛中,v......
  • 异步场景: promise、async函数与await命令介绍
    如果你也对鸿蒙开发感兴趣,加入“Harmony自习室”吧!扫描下方名片,关注公众号,公众号更新更快,同时也有更多学习资料和技术讨论群。在鸿蒙的开发中,我们时常会遇到promise异步场景,有同学反馈说希望提一下。异步开发这部分的内容比较多,我不确定这位朋友具体想讨论是哪些方面,那我从......
  • 【题目解析】蓝桥杯23国赛C++中高级组 - 斗鱼养殖场
    【题目解析】蓝桥杯23国赛C++中高级组-斗鱼养殖场题目链接跳转:点击跳转前置知识:了解过基本的动态规划。熟练掌握二进制的位运算。题解思路这是一道典型的状压动态规划问题。设\(dp_{i,j}\)表示遍历到第\(i\)行的时候,当前行以\(j_{(base2)}\)的形式排列乌龟可以构......