当前位置: 首页 > news >正文

同步与异步

核心概念:一个简单的比喻

想象一下你去咖啡店买咖啡:

  • 同步(Synchronous)

    • 你点了一杯手冲咖啡。

    • 你就站在柜台前等着什么也不做,看着店员一步步磨豆、冲泡。

    • 直到店员把咖啡递给你,你才离开去做下一件事。

  • 特点顺序执行,你必须等待当前任务彻底完成才能继续下一个。

  • 异步(Asynchronous)

  • 你点了一杯手冲咖啡。

  • 店员给你一个取餐号,告诉你好了会叫你。

  • 不用在柜台前傻等,可以去找个座位玩手机、看书、和朋友聊天。

  • 当咖啡做好,店员叫到你的号码时,你再去柜台取。

  • 特点无需等待,发起调用后,线程不会阻塞,可以继续执行其他任务。被调用者完成后会通过某种方式(如回调)通知你。

技术定义与区别

在系统编程中,我们讨论的通常是 I/O 操作(如读写文件、网络通信)或耗时任务(如复杂计算)。

特性同步异步
调用方式发起调用后,调用者主动等待结果返回。发起调用后,调用者立即返回,不等待结果。
控制流顺序的、线性的。代码逻辑就是执行顺序。非线性的、事件驱动的。代码逻辑可能被回调函数打断。
线程状态线程会被阻塞(Blocked),即线程暂停执行,让出CPU。线程不会阻塞,可以继续执行后续代码或其他任务。
性能影响浪费CPU时间在等待上,并发能力低(需要多线程来弥补)。CPU利用率高,单线程即可处理高并发I/O。
编程复杂度简单直观,易于理解和调试(try-catch即可处理错误)。更复杂,需要处理回调函数、状态管理,错误处理可能更麻烦。
典型例子read(), write(), connect() 等默认阻塞的系统调用。libuv (Node.js), asio (C++), epoll/kqueue+非阻塞I/O, 回调函数。

作用

同步模式的作用:

  • 优点:逻辑简单,代码易于编写、阅读和调试。符合人类的直线思维。

  • 缺点:性能瓶颈。对于I/O密集型的应用(如Web服务器、数据库),如果每个请求都用一个线程同步处理,线程会大量时间阻塞在I/O上。创建成千上万个线程会消耗大量内存(线程栈)和上下文切换的开销,导致系统性能急剧下降。

  • 同步模式的优化:为了缓解性能问题,通常会使用多线程或多进程模型(如Apache的prefork模式)。每个连接分配一个线程/进程,用更多的资源来支撑更多的并发连接。但这是一种“硬扛”的方式,有资源上限。

异步模式的作用:

  • 优点:极高的性能和可扩展性。特别适合I/O密集型应用。

  • 高并发:一个单线程的事件循环(Event Loop)就可以同时处理成千上万个网络连接(如Nginx, Redis, Node.js)。

  • 低资源消耗:避免了多线程的内存和上下文切换开销。

  • 缺点:“回调地狱”(Callback Hell),代码可读性差,流程难以追踪。不过现代编程语言用 Promise, async/await 等语法糖极大地缓解了这个问题。

  • 异步模式的核心:其高效性依赖于操作系统提供的 I/O 多路复用 机制(如 select, poll, epoll (Linux), kqueue (BSD/macOS))。这些机制允许一个线程监听大量文件描述符(如Socket)的状态,当某个描述符就绪(如可读、可写)时,才通知应用程序去处理,从而避免了盲目的阻塞等待。

在Linux系统编程中的具体实现

1.同步阻塞I/O

这是最默认、最简单的方式。

int fd = open("file.txt", O_RDONLY);
char buf[1024];
// 线程会阻塞在这里,直到从磁盘读取数据到缓冲区
ssize_t n = read(fd, buf, sizeof(buf)); 
// 只有read返回后,才能执行后面的代码
printf("Read %zd bytes\n", n);
close(fd);

2.异步I/O (使用 aio_* 系列函数)

Linux提供了原生的异步I/O接口(AIO)。

#include <aio.h>
// ...
struct aiocb cb = {0};
cb.aio_fildes = fd;
cb.aio_buf = buf;
cb.aio_nbytes = sizeof(buf);
cb.aio_offset = 0;// 发起异步读请求,函数立即返回,线程继续执行
aio_read(&cb);// ... 这里可以执行其他计算任务 ...// 之后再去检查读操作是否完成,或者等待完成
while (aio_error(&cb) == EINPROGRESS) {// 操作还在进行中
}
// 获取操作结果
ssize_t n = aio_return(&cb);

注意:Linux原生AIO在实践中用得并不广泛,因为API复杂且在某些情况下有局限性。

3.更常见的模式:I/O多路复用 + 非阻塞I/O
这是构建高性能异步网络应用最主流的方式。它并不是真正的“异步I/O”,而是通过非阻塞就绪通知来模拟出异步的行为。

  • 将文件描述符设为非阻塞(Non-blocking):
int flags = fcntl(socket_fd, F_GETFL, 0);
fcntl(socket_fd, F_SETFL, flags | O_NONBLOCK);

对非阻塞的fd调用read,如果没数据可读,会立即返回EAGAIN或EWOULDBLOCK错误,而不是阻塞。

  • 使用 epoll 监听就绪事件:
int epoll_fd = epoll_create1(0);
struct epoll_event event;
event.events = EPOLLIN | EPOLLET; // 监听可读事件,边缘触发模式
event.data.fd = socket_fd;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, socket_fd, &event);struct epoll_event events[MAX_EVENTS];
while (1) {// 等待事件发生。如果没有事件,线程会阻塞在这里,但它是同时监听所有fdint nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);for (int i = 0; i < nfds; i++) {if (events[i].events & EPOLLIN) {int fd = events[i].data.fd;// 我们现在知道这个fd肯定有数据可读了,这时再调用read不会阻塞ssize_t n = read(fd, buf, sizeof(buf));// 处理数据...}}
}

这就是Nginx、Redis等软件的核心工作原理。一个线程(主循环)高效地管理着所有连接。

小结

在这里插入图片描述

在现代系统编程中,异步编程模型是构建高性能、可扩展服务的基础。虽然它增加了代码的复杂性,但其带来的性能收益是巨大的。许多现代编程语言(如Go的Goroutine、Rust的async/await)都提供了更优雅的语法和工具来降低异步编程的难度。

http://www.dtcms.com/a/393343.html

相关文章:

  • C++中char与string的终极对比指南
  • Java基础 9.20
  • U228721 反转单链表
  • 串行总线、并行总线
  • `HTML`实体插入软连字符: `shy;`
  • 日志驱动切换针对海外vps日志收集的操作标准
  • Zynq开发实践(SDK之自定义IP2 - FPGA验证)
  • 广东电信RTSP单播源参数解析
  • 关于工作中AI Coding的一些踩坑经验
  • MyBatis 参数传递详解:从基础到复杂场景全解析
  • ego(8)---L-BFGS优化算法与B样条生成最终轨迹
  • 【开题答辩全过程】以 HPV疫苗预约网站为例,包含答辩的问题和答案
  • Linux网络中Socket网络套接字的高级应用与优化策略
  • 人才测评系统选型参考:含国内平台对比
  • 人才素质测评在线测评系统平台清单:5款推荐
  • 【语法进阶】匹配分组
  • 猫头虎AI开源项目分享:通过压缩-感知-扩展来改善RAG应用延迟的高效框架:REFRAG,速度快、质量高
  • 某音a_bogus纯算法192位研究分析
  • RAG vs 长文本模型:技术原理、适用场景与选型指南
  • PowerBI自定义函数
  • FreeRTOS——信号量,互斥锁,临界区,延时
  • 第三章 模型评估与优化技巧
  • 3.Spring AI的工具调用
  • 如何高效记单词之:学会想像——从字母W聊起
  • Python之Excel操作三:读取Excel文件中的某一列
  • 计网基础知识
  • 【CSP-J模拟题 】 附详细讲解
  • FPGA内实现FIR 抽取滤波器设计
  • 【proteus绿灯5s红灯10s三数码管数字切换电路】2022-12-12
  • 团队任务分配管理软件平台对比测评