首页 > 其他分享 >同步、异步、阻塞、非阻塞、回调函数

同步、异步、阻塞、非阻塞、回调函数

时间:2024-05-28 22:47:01浏览次数:23  
标签:异步 同步 函数 int 阻塞 read file

同步、异步、阻塞、非阻塞、回调函数

一、同步、异步和回调函数

1. 概念

程序在执行过程中会存在函数调用,区分同步和异步的关键点在于函数调用后主程序如何运行。

  • 同步:函数调用后,主程序等待着函数返回才会继续往下运行。

  • 异步:函数调用后,主程序不等待函数返回就继续往下运行。

下图示例的程序中,在调用sum函数时,主程序等待着sum函数返回才继续往下运行,这就是同步。

同步

下图示例的程序中,在调用delete_file函数时,主程序并没有等待delete_file函数返回就继续往下运行了,这就是异步。

既然异步的情况下,主程序不管子函数什么时候结束就继续往下执行了,那主程序什么时候知道子函数执行结束了呢?答案就是回调函数

那什么是回调函数呢?

我在上班的时候,接收的任务都是上司的左膀右臂下发的,他们把任务告诉我,我去实验室干活,干完以后他们让我发个 vx 消息告诉他们我干完了。他们不会守着我干活,也在忙自己的事。这个场景里面的左膀右臂就是主程序,我就是子函数,vx 通知他们就是回调函数。回调函数是主程序向子函数传输的 1 个函数指针(关于函数指针请 RTFW),用来告诉子函数在它完成后该执行哪些操作来通知主程序。

2. 举例

加班对于程序员来说是家常便饭(FUCK),某天晚上 11 点,你准备想下个早班,刚准备关鸡卸下戴了一天的面具重新做人,你直属上司突然从墙后面冒出来跟你说,”马工,现在还早,今晚加个班再多写几个 bug 吧“。说完,你上司从兜里拿出一包瓜子坐在你旁边嗑起来,行情不好,你又是个被世俗污染过的人(没个性),只好认命加班。

你上司(主程序)在你(子函数)旁边嗑瓜子等着你写 bug,等你写完才准你下班,这就是同步

第二天晚上,你已经连续加班 29 天了,再差 1 天就满 1 个月了,你决定不要达成这项可耻的成就,于是你打算偷偷溜走,没想到老板在电梯门口把你堵住了。”马工啊,这个月你确实辛苦了,但是程序明天上线,要不,你今晚加班守一下上线情况?“。你心里跑过一万只 ikun,但是也只能回去达成加班 1 个月成就,但是老板肯定是不用加班的,于是他走进电梯下班回家了。

你老板(主线程)要你加班,但是他自己开奔驰回家了,管你(子函数)加班到几点(不守着你),是死是活,等你写完 bug 后给老板发微信留个言报平安,这就是异步。发 vx 给老师报项目上线一切正常,这就是回调函数。

二、阻塞和非阻塞

1. 概念

阻塞、非阻塞同样是看主程序执行过程中,对函数调用后的情况来判断。区分阻塞和非阻塞的关键点在于函数调用后 CPU 的使用权是否会被转移

  • 阻塞:函数调用后,由于 I/O 等操作导致了 CPU 暂停执行本段程序。最常见的是磁盘读写操作导致的 CPU 被切换去执行其他进程。发生阻塞的情况基本上都涉及到系统调用
  • 非阻塞:函数调用后,CPU 不会因为此次调用而被切换去执行其他进程。

2. 举例

写代码的时候会使用到系统调用(RTFW),涉及到磁盘 I/O 的系统调用通常都会产生阻塞,比如readwriterecv等函数。

为什么会产生阻塞呢?

这是因为 CPU 和磁盘的速度差距有如天壤之别导致的(如果 CPU 的速度比作是战斗机,那么磁盘的读写速度就是肯德坤)。为了最大化 CPU 的执行效率,CPU 不会空等着磁盘读写完成,而是切换到其他进程去执行,等磁盘读写完成后再返回来继续执行主程序剩下的代码。由于 CPU 的使用权被转移,导致本程序被暂停执行,这种情况下就说程序由于这个函数调用被阻塞了

int main(int argc, char* argv[]) {
    FILE* file = open("file_path");
    int res = read(file);	// 此时程序被阻塞 n 秒
    process(file);			// 后面的程序在 read 函数返回后才能继续执行
    int res = write(file);
}

非阻塞就是指主程序在函数调用时不会导致 CPU 切换至其他进程上去执行(除非时间片切换)

long sum(int a, int b) {		// 调用 sum 求 1-10000 亿的和
    long sum = 0;
    for (int i = a; i <= b; ++i) {
        sum += i;
    }
    
    return sum;
}

如果调用 sum 求 1-10000 亿的和,可能要计算很久(应该也不用很久,几分钟?但是几分钟对于 CPU 来说已经算很久了),但是由于 CPU 一直在子函数里面运行(CPU 并没有被转移使用权去执行其他进程),所以成调用 sum 函数不会导致程序被阻塞,即 sum 函数是不阻塞的。

总结:阻塞和非阻塞与 CPU 的执行流是否被暂停有关

三、关系

同步、异步、阻塞、非阻塞这 4 者的关系分为同步阻塞、同步非阻塞、异步非阻塞。一般没有异步阻塞,除非出现程序 bug。

1. 同步阻塞

int main(int argc, char* argv[]) {
    FILE* file = open("file_path");
    int res = read(file);	// 此时程序被阻塞 n 秒
    process(res);			// 后面的程序在 read 函数返回后才能继续执行
    int res = write(file);
}

还是上面那个例子,只有在read函数返回以后才能读取到结果,进而对结果进行处理,所以这是同步阻塞的。

2. 同步非阻塞

同步不一定是阻塞的,但阻塞一定是同步的

long sum(int a, int b);
int main(int argc, char* argv[]) {
    int a = 1;
    int b = 100000000000000000000000000000000000000000;
    long res = sum(a, b);
    printf("hello,world");
}

上面的 sum 函数可能要执行好几分钟,并且得 sum 函数执行完成以后才会打印 hello,world,虽然执行时间长,但是 CPU 使用权没有被转移,所以这是同步非阻塞的。

3. 异步非阻塞

我们对同步阻塞的程序增加一个需求,那就是在读取文件以后,需要让用户进行输入。

int main(int argc, char* argv[]) {
    FILE* file = open("file_path");
    int res = read(file);	// 此时程序被阻塞 n 秒
    process(res);			// 后面的程序在 read 函数返回后才能继续执行
    int res = write(file);
    
    // 处理用户输入
    int in = input();
    process(in);
}

用户永远是上帝,如果现在还是使用read这种导致阻塞的函数,并且这段代码是你写的话,那么你即将得到解脱,黑眼圈、焦虑症、晚睡早起等症状都会得到解决。

为了让自己能继续打工(继续主动折磨自己),你决定修改代码为异步非阻塞

假设你的操作系统提供了一个异步非阻塞的_read函数,那么你可以这样写你的代码:

void callback();
int main(int argc, char* argv[]) {
    FILE* file = open("file_path");
    int res = _read(file, callback);	// 异步调用,callback 被当作回调函数传入 _read
    
    // 处理用户输入
    int in = input();
    process(in);
}

此时由于_read函数是异步调用的,那么调用_read后会立即被返回,就可以马上处理用户的输入啦!

这时候有同学就会问,那我怎么知道_read函数什么时候结束?注意看,我们给_read函数传入了回调函数callback这是库函数的开发者提供的接口,如果你使用的操作系统支持异步调用的话一定会有这样的标准接口。既然这样,那么_read函数在读取完磁盘的数据以后就会调用我们写的callback函数,那么我们就可以在callback函数里面去写我们拿到磁盘数据以后处理数据的操作逻辑(在主程序中已经省略掉了处理数据的代码)。当然这只是一个例子来说明异步调用,正常情况说callback函数会有参数(一般是指针)来携带出系统调用的数据,以供我们后期处理。

那么本篇讲解同步、异步、阻塞、非阻塞、回调函数的文章就到这里啦!

欢迎留言纠错!

标签:异步,同步,函数,int,阻塞,read,file
From: https://www.cnblogs.com/chiweiming/p/18219111

相关文章

  • 使用prometheus监测MySQL主从同步状态方案
    说明:本文介绍如何使用prometheus、alertmanager监测MySQL主从,当从节点中断同步时,发送邮箱报警,并使用grafana将数据视图化。结构图如下:安装(1)安装应用首先,来安装prometheus、alertmanager和grafana,参考以下文章(都是我写的,嘿嘿):CentOS7安装prometheusCentOS7安装aler......
  • 进程间同步(互斥锁)
    【一】什么是互斥锁互斥锁是一种用于多线程编程中控制对共享资源访问的机制限制当前时间段只能由当前进程使用,当前进程使用完成后才能其他进程继续使用基本原理是在对共享资源进行访问前加锁,使得其他线程无法访问该资源,当访问完成后再解锁,使得其他线程可以进行访问【......
  • 【go从入门到精通】精通并发编程-使用atomic管理状态和同步的无锁技术
    了解原子计数器        在Go中,原子计数器是多个goroutine可以同时访问的共享变量。术语“原子”是指在计数器上执行的操作的不可分割的性质。在Go中,原子计数器允许多个goroutine安全地更改共享变量,而无需使用锁或任何其他显式同步,这可确保数据完整性并避免竞......
  • 简单理解Zookeeper之数据同步机制
    写入数据流程请求发给Leaderclient向Zookeeper集群的Leader节点发送写请求Leader节点接收到写请求后,会对请求进行预处理,并为这次写操作分配一个全局唯一的递增ID(ZXID)。Leader将这个写请求(提案)广播给所有的Follower节点。这个提案包含了请求的具体内容和分配的ZXID。每个......
  • 异步数据加载与绑定设计: AsyncLoadHelper
    在现代应用程序开发中,异步数据加载和管理是一个普遍的需求。本文介绍了一个自定义的异步数据加载工具——AsyncLoadHelper。通过详细的设计和实现介绍,以及结合实际应用示例,展示了AsyncLoadHelper在简化异步操作、提高代码可读性和维护性方面的优势。在开发过程中,异步数据加载常常......
  • SD8906A恒定批量降压转换器集成电路IC同步整流器SOT封装
    该SD8906A是一个恒定频率,电流模式PWM降压转换器。该器件集成了一个主开关和一个同步整流器,无需外部肖特基二极管即可实现高效率。它是为单节锂离子(Li+)电池供电的便携式设备的理想选择。输出电压可调低至0.6V。该SD8906A还可以运行在100%的低压差操作占空比,延长便携式系统......
  • CDC 数据实时同步入湖的技术、架构和方案(截至2024年5月的现状调研)
    博主历时三年精心创作的《大数据平台架构与原型实现:数据中台建设实战》一书现已由知名IT图书品牌电子工业出版社博文视点出版发行,点击《重磅推荐:建大数据平台太难了!给我发个工程原型吧!》了解图书详情,京东购书链接:https://item.jd.com/12677623.html,扫描左侧二维码进入京东手......
  • SQLServer如何监控阻塞会话
    一、查询阻塞和被阻塞的会话SELECTr.session_idAS[BlockedSessionID],r.blocking_session_idAS[BlockingSessionID],r.wait_type,r.wait_time,r.wait_resource,s1.program_nameAS[BlockedProgramName],s1.login_nameAS[Blo......
  • SQLServer如何监控阻塞会话
    一、查询阻塞和被阻塞的会话SELECTr.session_idAS[BlockedSessionID],r.blocking_session_idAS[BlockingSessionID],r.wait_type,r.wait_time,r.wait_resource,s1.program_nameAS[BlockedProgramName],s1.login_nameAS[Block......
  • 第五章 并发基础中的Future异步回调模式
    案例:为了提升泡茶效率。下面分别是用阻塞模式和异步回调模式来实现其中的异步泡茶流程。为了异步执行整个泡茶流程,分别设计三条线程:主线程、清洗线程、烧水线程。(1)主线程(MainThread)的工作是:启动清洗线程、启动烧水线程,等清洗、烧水完成后,泡茶喝。(2)清洗线程(WashThread)的工作是:洗......