同步与异步,阻塞与非阻塞的深入分析

news2024/9/20 8:11:29

😎 作者介绍:欢迎来到我的主页👈,我是程序员行者孙,一个热爱分享技术的制能工人计算机本硕,人工制能研究生。公众号:AI Sun(领取大厂面经等资料),欢迎加我的微信交流:sssun902
🎈 本文专栏:本文收录于《动手学操作系统》系列专栏,相信一份耕耘一份收获,我会分享OS相关学习内容,不说废话,祝大家都offer拿到手软
🤓 欢迎大家关注其他专栏,我将分享Web前后端开发、人工智能、机器学习、深度学习从0到1系列文章。
🖥随时欢迎您跟我沟通,一起交流,一起成长、进步!

同步与异步,阻塞与非阻塞的深入分析

在计算机科学中,同步与异步、阻塞与非阻塞是描述程序行为的常用术语。理解这些概念对于编写高效、可靠的程序至关重要。本文将详细分析这些概念,并结合具体例子进行说明。
在这里插入图片描述

同步与异步

在这里插入图片描述

同步(Synchronous)

同步操作指的是请求发起方在发起请求后,必须等待结果返回才能继续执行其他任务。这种模式下,程序的执行是顺序的,一个任务完成后才能开始下一个任务。

特点:

  • 请求发起后,调用方必须等待结果。
  • 调用方在等待期间不能执行其他任务。

例子:

  • 普通B/S模式(同步):提交请求 -> 等待服务器处理 -> 处理完毕返回。在这个过程中,客户端浏览器不能执行其他任务。

异步(Asynchronous)

异步操作则是指请求发起后,调用方不需要立即等待结果,可以继续执行其他任务。当请求处理完成时,通过状态改变、消息通知或回调函数等方式通知调用方。

特点:

  • 请求发起后,调用方不需要立即等待结果。
  • 调用方可以在等待期间执行其他任务。

例子:

  • AJAX请求(异步):请求通过事件触发 -> 服务器处理(浏览器仍然可以执行其他任务) -> 处理完毕。

同步异步代码示例

在C++中,同步和异步的实现可以通过多种方式完成,例如使用标准库中的线程、互斥锁、条件变量等。以下是一些示例:

同步(Synchronous)代码示例

以下是一个简单的同步文件读取示例,使用C++标准库中的文件流进行同步IO操作:

#include <iostream>
#include <fstream>
#include <string>

std::string readFileSync(const std::string& filename) {
    std::ifstream file(filename);
    if (!file) {
        throw std::runtime_error("Unable to open file");
    }

    std::string content((std::istreambuf_iterator<char>(file)),
                         (std::istreambuf_iterator<char>()));
    return content;
}

int main() {
    try {
        std::string content = readFileSync("example.txt");
        std::cout << content << std::endl;
    } catch (const std::exception& e) {
        std::cerr << e.what() << std::endl;
    }

    return 0;
}

在这个示例中,readFileSync函数打开文件并同步读取其内容。这将阻塞,直到文件内容被完全读取。

异步(Asynchronous)代码示例

C++11引入了std::asyncstd::future,可以用来实现异步操作。以下是一个使用std::async进行异步文件读取的示例:

#include <iostream>
#include <fstream>
#include <string>
#include <future>

std::string readFileAsync(const std::string& filename) {
    std::ifstream file(filename);
    if (!file) {
        throw std::runtime_error("Unable to open file");
    }

    std::string content((std::istreambuf_iterator<char>(file)),
                         (std::istreambuf_iterator<char>()));
    return content;
}

int main() {
    auto future = std::async(std::launch::async, readFileAsync, "example.txt");

    try {
        std::string content = future.get();  // 等待异步操作完成
        std::cout << content << std::endl;
    } catch (const std::exception& e) {
        std::cerr << e.what() << std::endl;
    }

    return 0;
}

在这个示例中,std::async启动了一个异步任务来读取文件。future.get()会阻塞,直到异步操作完成并返回结果。

使用线程的异步示例

以下是一个更复杂的异步示例,使用C++11的线程库:

#include <iostream>
#include <fstream>
#include <string>
#include <thread>
#include <mutex>
#include <condition_variable>

std::string content;
std::mutex mtx;
std::condition_variable cv;
bool ready = false;

void readFileAsync(const std::string& filename) {
    std::ifstream file(filename);
    if (!file) {
        throw std::runtime_error("Unable to open file");
    }

    std::string local_content((std::istreambuf_iterator<char>(file)),
                               (std::istreambuf_iterator<char>()));
    std::lock_guard<std::mutex> lock(mtx);
    content = std::move(local_content);
    ready = true;
    cv.notify_one();
}

int main() {
    std::thread t(readFileAsync, "example.txt");

    std::unique_lock<std::mutex> lock(mtx);
    cv.wait(lock, []{ return ready; });

    t.join();  // 确保线程完成执行

    try {
        std::cout << content << std::endl;
    } catch (const std::exception& e) {
        std::cerr << e.what() << std::endl;
    }

    return 0;
}

在这个示例中,一个线程被用来异步读取文件。主线程通过std::condition_variable等待文件读取完成,然后输出内容。

示例展示了如何在C++中实现同步和异步操作。同步操作直接在主线程中完成,而异步操作则通过多线程或其他并发机制来实现。

阻塞与非阻塞

阻塞(Blocking)

阻塞调用是指在调用结果返回之前,当前线程会被挂起,直到结果返回。在阻塞期间,线程不能执行其他任务。

特点:

  • 调用结果返回之前,线程被挂起。
  • 线程在等待期间不能执行其他任务。

例子:

  • 在socket编程中,如果缓冲区中没有数据,调用recv函数会一直等待,直到有数据才返回。此时,当前线程会被挂起。

非阻塞(Non-blocking)

非阻塞调用则是指在不能立即得到结果之前,该函数不会阻塞当前线程,而会立即返回。调用方需要周期性地检查结果是否可用。

特点:

  • 调用结果未返回时,线程不会被挂起。
  • 线程在等待期间可以执行其他任务。

例子:

  • 如果将socket设置为非阻塞模式,调用recv函数时,如果缓冲区中没有数据,则系统调用立即返回,不会挂起线程。线程会继续轮询,直到socket内核缓冲区内有数据为止。

阻塞和非阻塞代码示例

在C++中,阻塞和非阻塞的概念通常与I/O操作相关,特别是网络编程和文件I/O。以下是使用C++进行套接字编程的阻塞和非阻塞示例。

阻塞(Blocking)I/O 示例

以下是一个使用阻塞I/O的C++网络客户端示例:

#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>

int main() {
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock == -1) {
        std::cerr << "Socket creation failed" << std::endl;
        return 1;
    }

    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(8080); // 假设服务器监听在8080端口
    server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 服务器地址

    if (connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        std::cerr << "Connection failed" << std::endl;
        return 1;
    }

    const char *message = "Hello, Server!";
    if (send(sock, message, strlen(message), 0) < 0) {
        std::cerr << "Send failed" << std::endl;
        return 1;
    }

    char buffer[1024] = {0};
    if (recv(sock, buffer, sizeof(buffer), 0) < 0) {
        std::cerr << "Receive failed" << std::endl;
        return 1;
    }

    std::cout << "Server response: " << buffer << std::endl;
    close(sock);
    return 0;
}

在这个示例中,connectsendrecv函数都是阻塞调用。如果它们不能立即完成,比如服务器没有立即响应,调用线程将会被阻塞,直到操作完成。

非阻塞(Non-blocking)I/O 示例

要设置套接字为非阻塞模式,可以使用fcntl函数:

#include <fcntl.h>

以下是使用非阻塞I/O的C++网络客户端示例:

// ...(其他包含和定义与上面相同)

int setNonBlocking(int sock) {
    int opts = fcntl(sock, F_GETFL);
    if (opts < 0) {
        std::cerr << "fcntl failed" << std::endl;
        return -1;
    }
    opts = opts | O_NONBLOCK;
    if (fcntl(sock, F_SETFL, opts) < 0) {
        std::cerr << "fcntl failed" << std::endl;
        return -1;
    }
    return 0;
}

int main() {
    // ...(创建套接字的代码与上面相同)

    if (setNonBlocking(sock) < 0) {
        return 1;
    }

    // ...(设置服务器地址的代码与上面相同)

    if (connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        if (errno != EINPROGRESS) {
            std::cerr << "Connection failed" << std::endl;
            return 1;
        }
        // 连接正在进行中,可以继续其他任务
    }

    // ...(发送消息的代码与上面相同)

    char buffer[1024] = {0};
    while (recv(sock, buffer, sizeof(buffer), 0) < 0) {
        if (errno != EAGAIN && errno != EWOULDBLOCK) {
            std::cerr << "Receive failed" << std::endl;
            break;
        }
        // 没有数据可读,可以继续其他任务或轮询
    }

    // ...(打印服务器响应和关闭套接字的代码与上面相同)
}

在这个示例中,通过setNonBlocking函数将套接字设置为非阻塞模式。当connectsendrecv调用不能立即完成时,它们会立即返回而不是阻塞调用线程。

组合模式

同步阻塞(Synchronous Blocking)

同步阻塞模式下,调用方在发起请求后,必须等待结果返回,且在等待期间不能执行其他任务。

例子:

  • 去照相馆拍照,拍完照片后,商家说需要30分钟才能洗出来照片。如果顾客在店里等待照片洗好,这个过程就是同步阻塞。

同步非阻塞(Synchronous Non-blocking)

同步非阻塞模式下,调用方在发起请求后,可以立即返回,但需要周期性地检查结果是否可用。

例子:

  • 顾客在照相馆拍照后,继续在店里刷手机,时不时地询问老板照片是否洗好,这个过程就是同步非阻塞。

异步阻塞(Asynchronous Blocking)

理论上,异步阻塞模式是不存在的。因为异步操作本身就是为了不阻塞调用方,如果还要等待结果,就失去了异步的意义。

异步非阻塞(Asynchronous Non-blocking)

异步非阻塞模式下,调用方在发起请求后,可以继续执行其他任务,当请求处理完成时,通过回调或其他方式通知调用方。

例子:

  • 顾客在照相馆拍照后,留下手机号,然后去公园等待老板的电话通知照片洗好,这个过程就是异步非阻塞。

图解

以下是对这些模式的图解说明:

  1. 同步阻塞
    • Sender 发送请求后,等待 Receiver 返回结果,期间 Sender 被阻塞,不能处理其他任务。

在这里插入图片描述

  1. 同步非阻塞

    • Sender 发送请求后,立即返回,然后不断轮询 Receiver,直到收到结果。Sender 在等待期间可以处理其他任务。
  2. 异步非阻塞

    • Sender 发送请求后,立即返回,等待 Receiver 的回调。Sender 在等待期间可以处理其他任务。

在这里插入图片描述图借鉴于csdn

祝大家学习顺利~
如有任何错误,恳请批评指正~~
以上是我通过各种方式得出的经验和方法,欢迎大家评论区留言讨论呀,如果文章对你们产生了帮助,也欢迎点赞收藏,我会继续努力分享更多干货~


🎈关注我的公众号AI Sun可以获取Chatgpt最新发展报告以及腾讯字节等众多大厂面经
😎也欢迎大家和我交流,相互学习,提升技术,风里雨里,我在等你~


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

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

相关文章

人在迷茫无聊时该干什么?

做两件事&#xff0c;收和放。 第一件事收&#xff0c;全面收缩。 迷茫不是无事可做&#xff0c;而是所做的事没有意义&#xff0c;没有意义就停掉&#xff0c;空出时间让身心恢复到正常状态。迷茫会让你焦虑&#xff0c;让你晚睡&#xff0c;让你狂吃&#xff0c;让你迫不及待…

公司里的IT是什么?

公司里的IT是什么&#xff1f; 文章目录 公司里的IT是什么&#xff1f;1、公司里的IT2、IT技术3、IT行业4、IT行业常见证书 如果对你有帮助&#xff0c;就点赞收藏把&#xff01;(&#xff61;&#xff65;ω&#xff65;&#xff61;)&#xff89;♡ 前段时间&#xff0c;在公…

ARMv8 内存属性

目录 普通内存 (normal memory)设备内存&#xff08;device memory&#xff09;arm64 mair_el1 系统寄存器及linux 对其配置页表中的内存属性总结 普通内存 (normal memory) 弱一致性&#xff08;weakly ordered)。 存在分支预测&#xff0c;数据预取&#xff0c;高速缓存行预…

二分算法及其公式

二分查找 二分查找是大多数人第一个接触到的算法&#xff0c;很多人都认为只有有序的数组可以使用二分查找&#xff0c;但这种思想其实是错误的&#xff0c;二分查找是可以用于拥有二段性的数组&#xff0c;而且二分算法是由模板做参考的&#xff0c;所以只要掌握就可以解决大…

【区块链+绿色低碳】基于区块链的双碳能源纳管平台 | FISCO BCOS应用案例

在双碳战略的指导下&#xff0c;南京区块链产业应用协会牵头研发的双碳能源纳管平台&#xff0c;依托区块链、人工智能、云计算、 物联网、大数据、工业互联网与边缘计算等技术&#xff0c;对绿电追溯、需求侧响应、能源微网、源网荷储、隔墙用电、 碳排放权认证、额度计量、预…

矩阵常见分解算法及其在SLAM中的应用

文章目录 常见特殊矩阵定义Cholesky分解&#xff08;正定Hermittian矩阵&#xff0c;分解结果唯一&#xff09;Cholesky分解应用 SVD分解&#xff08;将singularvalues排序后分解唯一&#xff09;SVD 分解的应用&#xff08;任意矩阵&#xff09; QR分解&#xff08;任意矩阵&a…

第六周:机器学习周报

机器学习周报 摘要Abstract机器学习——类神经网络训练不起来怎么办&#xff1f;1. 自动调整学习率&#xff08;learning rate&#xff09;1.1 特制化的Learning Rate——parameter dependent1.1.1 Root Mean Square&#xff08;RMS&#xff0c;均方根&#xff09;1.1.2 RMSPro…

【Python】基础语法(下)

本篇文章将接着上篇文章继续讲解基础语法&#xff1a; &#xff08;4&#xff09;变量 &#xff08;5&#xff09;注释 &#xff08;6&#xff09;输入 &#xff08;7&#xff09;条件语句 四&#xff1a;变量 变量其实就是我们生活中起别名和外号。让变量名指向某个值&a…

旅游卡,免费,旅游是真的吗?真相是……

但这种包来回大交通&#xff0c;一旦成本大于利润&#xff0c;他们就会以各种理由推卸责任。这就是我在“揭秘&#xff1a;共享旅游卡免费旅游&#xff0c;包来回路费&#xff0c;这背后的3大真相&#xff01;”这篇文章里面讲到那个大妈的惨痛教训。 以上这5点真相&#xff0…

Python 中的@符号:如何用装饰器改变你的编程方式?

Python 是一种强大且灵活的编程语言&#xff0c;其中有许多独特的语法元素和概念。 符号通常用于装饰器。它看起来可能有些神秘&#xff0c;但实际上它的工作原理非常简单。 什么是装饰器&#xff1f; 在了解 符号之前&#xff0c;我们首先需要理解什么是装饰器。简单来说&am…

C++设计模式笔记(内附可运行代码示例)

持续更新, 欢迎关注....... 前言 设计目的 高内聚&#xff0c;低耦合 设计原则 1、开放封闭原则 类的改动是通过增加代码进行&#xff0c;而不是修改源代码。 2、单一职责原则 职责单一&#xff0c;对外只提供一种功能&#xff0c;引起类变化的原因都应该只有一个。 3…

【中项】系统集成项目管理工程师-第9章 项目管理概论-9.1PMBOK的发展与9.2项目基本要素

前言&#xff1a;系统集成项目管理工程师专业&#xff0c;现分享一些教材知识点。觉得文章还不错的喜欢点赞收藏的同时帮忙点点关注。 软考同样是国家人社部和工信部组织的国家级考试&#xff0c;全称为“全国计算机与软件专业技术资格&#xff08;水平&#xff09;考试”&…

数据库设计效率提高的5大注意事项

数据库设计效率和质量的提高对项目影响深远&#xff0c;能够显著提升数据访问速度&#xff0c;确保数据一致性和完整性&#xff0c;减少应用开发和维护成本&#xff0c;同时提升系统稳定性和用户体验。如果数据库设计不佳会导致项目性能低下&#xff0c;数据访问缓慢&#xff0…

Java7.0标准之重要特性及用法实例(十八)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 新书发布&#xff1a;《Android系统多媒体进阶实战》&#x1f680; 优质专栏&#xff1a; Audio工程师进阶系列…

APDL(ANSYS Parametric Design Language)初识

APDL&#xff08;ANSYS Parametric Design Language&#xff09;编写涉及使用ANSYS的参数化设计语言来创建、修改和执行有限元分析&#xff08;FEA&#xff09;任务。以下是一些关于APDL编写的基本步骤、技巧和示例&#xff1a; 一、基本步骤 了解APDL基础&#xff1a; 熟悉AP…

并发--快速查询死锁信息

使用jstack查看线程堆栈信息 jstack&#xff1a;jdk提供的一个工具&#xff0c;可以查看java进程中线程堆栈信息。 位于&#xff1a;jdk1.8.0_121\bin包下 死锁代码 public class DeadLockDemo {private static String A "A";private static String B "B"…

视频平台麓战奥运经济,谁能接住这“破天的富贵”?

文丨郭梦仪 与巴黎奥运会炸裂开幕式的“松弛感”不同&#xff0c;赛场外的流量之争早已硝烟弥漫。 今年&#xff0c;腾讯、咪咕、快手、抖音与中央广播电视总台达成奥运转播版权合作&#xff0c;长短视频平台各占一半。 而今&#xff0c;获得转播权的视频平台们&#xff0c;…

20240731 每日AI必读资讯

&#x1f4f1;苹果AI版iOS首日火爆&#xff1a;聊天秒变高情商&#xff0c;大模型成最强嘴替&#xff0c;Siri华丽变身 - 苹果的Apple Intelligence终于面世&#xff01;随着iOS 18.1 Beta版的上线&#xff0c;注册开发者从即日起就能体验到苹果AI的部分功能。 - Siri的全面换…

出行方案,智能推荐:用友BIP商旅云6.0推出AI新装备

随着企业业务的不断拓展和员工出行需求的日益复杂化&#xff0c;传统的商旅预订方式已经难以应对&#xff0c;同时企业在商旅成本控制方面也面临着巨大的挑战。为此用友BIP商旅云6.0推出了创新性的AI新装备——智能推荐&#xff0c;以智能分析与精准预测&#xff0c;为企业提供…

基于springboot的大学奖学金评定管理系统表结构调试讲解源码

基于springboot的大学奖学金评定管理系统 赠送自己录的运行教程视频&#xff0c;无经验也可以运行起来。 提供远程调试服务&#xff0c;加钱可远程帮忙运行起来。 项目功能: 二、项目功能介绍 管理员 个人中心&#xff1a;这是管理员的个人工作区域&#xff0c;允许管理员…