关于c#中异步async和await的理解

之前给大家介绍了所谓异步编程的用法,但是没有细致的理解到,今天想和大家一起探讨一下;

前文: 

C#笔记14 异步编程Async,await,task类-CSDN博客

异步的起初

应用程序会启动一个进程,一个进程可以有很多线程。

如果一个线程,我们给他命名为主线程。

这个线程如果一直往下走,只有一条线,这就是单线程。

还是这个线程,如果它运行的途中创建了新的小伙伴去执行其他任务,这就是多线程。

现在我们的主线程正在做着自己的事情类似于展示一个界面之类的,此时创建一个线程去后台执行其他任务,主线程不去管它,这就是异步。

所以多线程的起初就是异步的。

同步的出现

现在,主线程创建了两个小弟,两个小弟功能差不多,这两个小弟为了抢占一个资源,打起来了。这可不行对吧,于是我们就设立各种同步机制和同步锁,控制他们学会等待

等待的方式各不相同,但是毫无疑问,线程间互相有了约束和阻塞。

到这里其实我们想做的事情已经基本完成了。

线程各司其职遇到资源就进行同步机制的等待和唤醒操作。

但是还是会有问题,比如多线程的管理,小弟太多了,根本管不过来,小弟之间使用资源的调度原则不够公平等等。当然了,这要求程序员的管理和操作系统的调度配合。

除此之外也发现管理多线程的方式不够先进:

管理的困难

主线程是老大,今天派小弟出去办事,小弟办事之后,确实办完了,但是主线程不知道小弟已经办完了,这时候是不是有小伙伴要想到C#中的委托和事件了?

这样就可以让小弟通知老大哥自己做好了,老大哥订阅了小弟的这个事件,这样就能在小弟完成事件的时候执行对应的方法了。

但是每一个小弟都这样,老大哥要手动管理这些事件,就好像老大哥每天都得坐在办公室听汇报了一样,尤其是管理线程的我们觉得很烦,这个组织太杂乱了!

尤其是什么情况?需要反复派出小弟去干活的时候,比如密集的io环境,我们还要和每个小老弟都约定一个事件,啥时候通知老大哥。

尤其是线程一多,老大哥虽然不迷糊但是老大哥也很烦,但是我们程序员,也就是负责真正管理小弟的人迷糊了。这可怎么办?

此时:

如果不派小弟去干,自己干,老大哥自己的事情就做不好了。(阻塞)

如果派出小弟,让小弟自己干自己的,老大又不知道到底干的什么情况了,就算小弟已经把结果搞出来了,已经通过什么形式提交了也不知道。(多线程异步)

如果手动告诉小弟怎么通知老大,这下倒是可以了,但是咱们秘书(程序员)可怜了,天天就负责管理每个小弟,告诉小弟在哪告诉老大,然后记下老大小弟给的数据怎么处理,用什么方法处理。(事件通知的多线程)

到这我们秘书就想到一个方法减轻自己的工作量。将社团改组,大哥和小弟都不是按所谓线程来做事了,而是新的机制或者说不同的任务处理者Task。

新的处理异步的方法

async和await出场!

async:用于标记一个方法是异步的,告诉编译器这个方法会有异步操作。该方法仍然可以在线程池中调度执行任务,但这个并不由 async 本身控制。
await:用于等待异步操作完成,而不会阻塞当前线程。它只会让出控制权,让当前线程去做其他事情,当操作完成时,再继续执行后续代码。

async标记一个事情,告诉老大和小弟,这个事情是异步的,这个事情要让小弟去干,不能影响老大哥工作。所以这个方法使用之后会立刻返回一个任务的引用作为结果,等到事情办完了这个结果才会被填充为指定的值。

await专门用来调用一个需要耗时间的操作,调用之后会让出控制权,等到再次获取控制权也就是后面等待的操作有结果的时候,如果异步方法有返回值的话,await还会自动把值从结果里拿出来给指定的对象。

老大哥调用异步方法就像派出一个小弟,task作为方法的返回值就像小弟给大哥留的纸条,等到小弟任务完成了,这个纸条上就会出现结果。这中间呢纸条就放在这里,。

await是逻辑上的等待这个结果,如果这个结果没有出现,后面的逻辑就不会执行。

意思是老大虽然后面有些事要等待这个事情,不代表老大就抛下自己手上的事情了,实际上老大还是在干活的。

你会发现小弟中间也会有一些await操作。

老大哥一调用这个事情啊,可以使用task,意思就是老大哥派出了这个小弟,但是自己没有去干奥,这个小东西就是小弟留给老大的小纸条,

await就是小弟告诉老大:“好,现在该到这里了,我得等某个任务完成了才能继续。

意思是什么?小弟办事也不是说立刻就办完了。也就是我们的方法内部也许还有耗时间的更具体的操作,比如老大让小弟读写一批文件,小弟可能要一个个文件读写,这就是小弟给老大的纸条。

await 就像说:“等这个任务做完了,再通知我,我再继续往下走。”

所以,await 的作用是让程序在某个任务完成之前,不阻塞其他操作。它并不会傻傻等着,而是会继续处理其他的事情,等到这个耗时操作完成后,再回到你指定的位置继续执行。

using System;
using System.Net.Http;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        Console.WriteLine($"{DateTime.Now:HH:mm:ss} - 大哥:开始做事,我让小弟去下载文件");

        // 大哥派小弟去干下载文件的活
        Task downloadTask = 小弟去下载文件Async("http://example.com/largefile");

        // 大哥自己继续干活
        for (int i = 0; i < 10; i++)
        {
            Console.WriteLine($"{DateTime.Now:HH:mm:ss} - 大哥:我在忙别的事... {i}");
            await Task.Delay(1000);  // 模拟大哥处理别的事情需要一些时间
        }

        // 等小弟把下载任务做完
        await downloadTask;

        Console.WriteLine($"{DateTime.Now:HH:mm:ss} - 大哥:小弟终于把文件下载完了,事情都做完了,收工!");
    }

    // 小弟去下载文件,过程可能很久
    static async Task 小弟去下载文件Async(string url)
    {
        Console.WriteLine($"{DateTime.Now:HH:mm:ss} - 小弟:开始下载文件...");

        using (HttpClient client = new HttpClient())
        {
            // 模拟下载文件的耗时操作
            await Task.Delay(5000); // 假装下载需要5秒钟
            Console.WriteLine($"{DateTime.Now:HH:mm:ss} - 小弟:文件下载完了!");
        }
    }
}

10:00:00 - 大哥:开始做事,我让小弟去下载文件
10:00:00 - 小弟:开始下载文件...
10:00:00 - 大哥:我在忙别的事... 0
10:00:01 - 大哥:我在忙别的事... 1
10:00:02 - 大哥:我在忙别的事... 2
10:00:03 - 大哥:我在忙别的事... 3
10:00:04 - 大哥:我在忙别的事... 4
10:00:05 - 小弟:文件下载完了!
10:00:05 - 大哥:我在忙别的事... 5
10:00:06 - 大哥:我在忙别的事... 6
10:00:07 - 大哥:我在忙别的事... 7
10:00:08 - 大哥:我在忙别的事... 8
10:00:09 - 大哥:我在忙别的事... 9
10:00:09 - 大哥:小弟终于把文件下载完了,事情都做完了,收工!

可以看出来,大哥在派出小弟之后自己还在做事,事实上小弟此时也正在下载文件。(注意,这里的小弟不是某个特定的我们管理的线程,而是被我们的系统指派的线程池甚至就是操作系统在做。)

大哥的await就是等小弟。

小弟的await就是小弟等小弟。

注意,await不会让后面的代码提前执行,反而是会就像顺序执行一样来执行。

但是它这种等待和我们之前线程的等待又不太一样,因为这种等待是代码让出控制权给其他方法去执行。

异步方法之间的依赖

小弟之间也存在依赖的时候呢,就可以像下面一样了。

using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        Console.WriteLine($"{DateTime.Now:HH:mm:ss} - 老大哥:开始派小弟A去下载文件");

        // 小弟A开始下载文件,小弟B等他下载完
        Task 小弟A的任务 = 小弟A去下载文件Async();
        Task 小弟B的任务 = 小弟B等小弟A再处理文件Async(小弟A的任务);

        Console.WriteLine($"{DateTime.Now:HH:mm:ss} - 老大哥:我先干点其他的事...");

        // 等两个任务都完成
        await Task.WhenAll(小弟A的任务, 小弟B的任务);

        Console.WriteLine($"{DateTime.Now:HH:mm:ss} - 老大哥:小弟们都完成了,我收工!");
    }

    static async Task 小弟A去下载文件Async()
    {
        Console.WriteLine($"{DateTime.Now:HH:mm:ss} - 小弟A:开始下载文件...");
        await Task.Delay(3000);  // 模拟耗时的下载过程
        Console.WriteLine($"{DateTime.Now:HH:mm:ss} - 小弟A:文件下载完了!");
    }

    static async Task 小弟B等小弟A再处理文件Async(Task 小弟A的任务)
    {
        Console.WriteLine($"{DateTime.Now:HH:mm:ss} - 小弟B:我得等小弟A下载完才能干活...");
        await 小弟A的任务;  // 等待小弟A的任务完成
        Console.WriteLine($"{DateTime.Now:HH:mm:ss} - 小弟B:现在小弟A下载完了,我开始处理文件...");
        await Task.Delay(2000);  // 模拟文件处理时间
        Console.WriteLine($"{DateTime.Now:HH:mm:ss} - 小弟B:文件处理完了!");
    }
}

事实上,任务机制可以让我们像写同步代码一样来写一些需要异步的操作。

同样的代码逻辑,实际的执行上就会不同。

这和传统线程管理的方式不同,避免了管理线程和事件订阅以及那种嵌套回调函数的麻烦。

这只是当下我浅薄的理解,如果我说的有错,请帮我指出,感谢。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

golang操作mysql利器-gorm

1、傻瓜示例 GORM通过将数据库表中的数据映射到面向对象的模型中&#xff0c;简化了数据库操作&#xff0c;使得开发者可以很方便的使用代码来操作数据库&#xff0c;而无需编写SQL语句。 目前有个mysql表&#xff1a;miniprogram_orders&#xff0c;其存储了所有用户对应的订…

Android SystemUI组件(07)锁屏KeyguardViewMediator分析

该系列文章总纲链接&#xff1a;专题分纲目录 Android SystemUI组件 本章关键点总结 & 说明&#xff1a; 说明&#xff1a;本章节持续迭代之前章节的思维导图&#xff0c;主要关注左侧上方锁屏分析部分即可。 为了更好理解本文的内容&#xff0c;优先说明下SystemUI中与Ke…

CoreDNS实现跨集群service解析实践

CoreDNS实现跨集群service解析实践 背景介绍使用条件实现方案 CoreDNS是一款使用Go语言实现的专为云原生应用而生的DNS服务器。本文介绍CoreDNS在特定实际场景下的一种进阶使用实践&#xff0c;也许能为其他也在使用CoreDNS做服务发现的同学提供一些启发和思考。 背景介绍 在…

luceda ipkiss教程 76:设计光栅耦合器

案例分享&#xff1a;设计光栅耦合器 全部代码如下&#xff1a; from si_fab import all as pdk from ipkiss3 import all as i3 import numpy as npclass grating_coupler(i3.PCell):"""SOI grating coupler."""_name_prefix "grating_c…

mysql 05 InnoDB数据页结构

01.数据页结构的快速浏览 02.记录在页中的存储 在页的7个组成部分中&#xff0c;我们自己存储的记录会按照我们指定的 行格式 存储到 User Records 部分。但是在一开始生成页的时候&#xff0c;其实并没有 User Records 这个部分&#xff0c;每当我们插入一条记录&#xff0c…

单词记忆的化境:用思想的流水去淹没坚硬的石块

其实&#xff0c;鹅卵石通常都是很硬的。但是河底的石子&#xff0c;几乎大多都成了鹅卵石&#xff0c;它们被流水淹没&#xff0c;日复一日、夜以继日的冲刷着&#xff0c;没有了棱角。 在单词的记忆过程中&#xff0c;我们有太多的人&#xff0c;都有着不堪回首的往事&#x…

面试系列-携程暑期实习一面

Java 基础 1、Java 中有哪些常见的数据结构&#xff1f; 图片来源于&#xff1a;JavaGuide Java集合框架图 Java 中常见的数据结构包含了 List、Set、Map、Queue&#xff0c;在回答的时候&#xff0c;只要把经常使用的数据结构给说出来即可&#xff0c;不需要全部记住 如下&…

爬虫逆向学习(九):记录一个集cookie、请求参数、请求体、响应文本加密的站点反爬

此分享只用于学习用途&#xff0c;不作商业用途&#xff0c;若有冒犯&#xff0c;请联系处理 反爬前置信息 站点&#xff1a;aHR0cHM6Ly96d2Z3LmNxLmdvdi5jbi9pY2l0eS9pY2l0eS9lbmdpbmVlcmluZy9uYXZpZ2F0aW9u 接口&#xff1a;/icity/api-v2/cq.app.icity.engineering.Engine…

江科大51单片机

文章目录 led灯led点亮led闪烁流水灯 独立按键按键点灯按键消抖按键实现二进制流水灯按键实现流水灯 数码管静态数码管显示动态数码管显示 矩阵键盘定时器/中断串口通信led点阵屏DS1302实时时钟蜂鸣器AT24C02DS18B20LCD1602直流电机驱动AD/DA红外遥控 led灯 创建项目&#xff…

打印沙漏(最蠢的办法)

直接给代码&#xff0c;很好理解的 #include<bits/stdc.h> using namespace std; int s(int b){if(b<1)return 0;if(b2)return 1;for(int i3;i<sqrt(b);i){if(b%i0)return 0;}return 1; } int main(){int n;cin>>n;char c;cin>>c;vector<int>s;…

网络原理之IP协议(网络层)

目录 前言 什么是IP协议&#xff1f; IP协议的协议头格式 16位总长度&#xff08;字节数&#xff09; 16位标识、3位标志位和13位片偏移 8位生存时间 IP地址管理 1.动态分配IP 2.NAT机制&#xff08;网络地址转换&#xff09; NAT机制是如何工作的 NAT机制的优缺点…

解决启动docker desktop报The network name cannot be found的问题

现象 deploying WSL2 distributions ensuring main distro is deployed: checking if main distro is up to date: checking main distro bootstrap version: getting main distro bootstrap version: open \wsl$\docker-desktop\etc\wsl_bootstrap_version: The network name…

Windows驱动调试方法

单步调试驱动 驱动的调试不能直接在本机上进行&#xff0c;而是要放在虚拟机&#xff08;或其它设备&#xff09;中。这是因为在内核模式下&#xff0c;一个断点的触发将会停下整个系统而不只是单个进程。 在前面的文章里&#xff0c;使用了DbgPrint函数来进行日志的输出&…

基于python+django+vue的旅游景点数据分析系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码 精品专栏&#xff1a;Java精选实战项目…

Elasticsearch 分片迁移与移除集群节点操作

Elasticsearch 分片迁移与移除集群节点操作 问题背景 在单台服务器上部署了 7 个 Elasticsearch 节点&#xff0c;分别为 es-node1 到 es-node7&#xff0c;端口从 9201 到 9207。每个节点都承载大量数据&#xff0c;但没有设置副本分片。由于多个节点共享同一台服务器的硬件…

论文阅读 - SWATTING Spambots: Real-time Detection of Malicious Bots on X

https://web.archive.org/web/20240523035749id_/https://dl.acm.org/doi/pdf/10.1145/3589335.3651564 目录 ABSTRACT INTRODUCTION METHODOLOGY 3 RESULTS ABSTRACT 在 X&#xff08;前身为 Twitter&#xff09;等社交网络平台上&#xff0c;垃圾邮件机器人的活动日益…

【SpringBoot整合Redis测试Redis集群案例】

1、第一步&#xff0c;创建springboot项目&#xff0c;并导入依赖 如图&#xff0c;创建项目遇到的第一个问题就是&#xff0c;当type选择maven&#xff0c;jdk选择1.8时&#xff0c;java部分没办法选择1.8的版本&#xff0c;这怎么办呢&#xff1f; 原因&#xff1a;搜了一下…

【windows 11 安装maven】

从下载网址下载maven 解压&#xff08;路径无中文&#xff09; 配置本地仓库&#xff0c;用来存储jar包 配置仓库路径 配置文件路径&#xff1a;./conf/settings.xml(conf文件夹中的settings.xml文件中&#xff09; 定位到53行修改 <!-- localRepository| The path to the l…

第 1 章:Vue 核心

1. Vue 简介 1.1. 官网 英文官网: https://vuejs.org/中文官网: https://cn.vuejs.org/&#xff1a;中文官网里面【教程】和【API】是比较重要的。用到api就去查询&#xff0c;实践当中记忆更牢靠。 风格指南&#xff1a;官方推荐写的一个代码风格cookbook&#xff1a;编写v…

从更底层的角度理解网站的访问过程

文章目录 1.示例&#xff0c;访问www.baidu.com是如何返回数据的1.输入www.baidu.com回车2.检查本机的C:\Windows\System32\drivers\etc\hosts配置文件夹下有没有这个域名对应的映射&#xff1a; 1.示例&#xff0c;访问www.baidu.com是如何返回数据的 1.输入www.baidu.com回车…