%% # 纲要 > 主干纲要、Hint/线索/路标 # Q&A #### 已明确 #### 待明确 > 当下仍存有的疑惑 **❓<font color="#c0504d"> 有什么问题?</font>** ###### 每个线程占有的独立的资源是什么? 每个线程都需要**独立的栈空间**,各自占有专有的内存区域,包括**栈数据段**和**堆数据段**。 # Buffer ## 闪念 > sudden idea ## 候选资料 > Read it later %% # 并发与并行 **并发**和 **并行** 在多任务处理上相似,但**本质不同**。 - ==**并发**==(Concurrency):多个任务在**同一时间段内执行**,但不一定同时执行。 - **==并行==**(Parallelism):多个任务 "**同时**" 执行 **"并行" 是严格意义上的 "==同时执行=="**,而 **"并发" 是包含前者、但更为宽松的一个概念**。 并发关注的是任务管理和调度,并行关注的是如何利用多核 CPU 的计算能力。 ![[_attachment/02-开发笔记/05-操作系统/并发相关/并发与并行.assets/IMG-并发与并行-3A13833F786C5B862BDE0E43F5D4F627.png|525]] > [!info] 并发 > > **==并发==**:两个或多个任务的**整个 "逻辑流/指令流" 从开始到结束的执行时间** 上存在重叠,称之为并发。 > > 在单核 CPU 系统中,虽然实际上**CPU 核心在任意时刻只能执行一个任务**,但操作系统通过**时间片轮转**等技术,**在多个任务之间多次切换**,**每个任务每次只处理一小部分**,从表现上而言让用户感觉到多个任务是同时执行的。 > > ![[_attachment/02-开发笔记/05-操作系统/并发相关/并发与并行.assets/IMG-并发与并行-3578E8857724F76360404C6EACC78913.png|421]] > [!info] 并行 > > **==并行==**:两个或多个任务 **==同时并行执行==**。 > > 在多核 CPU 系统中,**不同的任务可以被分配到不同的 CPU 核心上**,从而实现真正的同时执行。 <br><br> # 并发编程的实现方式 并发编程的实现方式有两种: - **多进程并发**:开销大——**进程间通信依赖于 OS 提供的机制**,依赖于 OS 进行进程管控。 - **==多线程并发==**:开销小——由于同一进程中所有线程共享进程的资源,**线程间通信/同步更简易**,**且线程切换开销小**。 ![[_attachment/02-开发笔记/05-操作系统/并发相关/并发与并行.assets/IMG-并发与并行-23D2E8470CD7BF22CA88BE9BC6C3CAE9.png|525]] > [!NOTE] **C++本身不支持进程间通信**,要实现进程间通信依赖于 OS 提供的 API。 <br> ## 多线程并发的好处 **不同任务的代码逻辑**之间明确分离,每个线程只负责执行一个独立任务。 > [!example] 多线程并发编程的优点:关注点分离 > > 以 DVD 播放软件为例,软件具有两个职责: > > - 从碟片读取数据,解码声音影像,并将其及时传送给图形硬件和音效硬件,让 DVD 顺畅放映; > - 接收用户的操作输入,譬如用户按“暂停”、“返回菜单”、“退出”等键。 > > 如果采取单一线程,则**该应用软件在播放过程中,不得不定时检查用户输入**,结果会混杂播放 DVD 的代码与用户界面的代码。 > > 如果采用多线程,则可**分离两个关注点**:**一个线程只负责用户界面管理**,**另一个线程只负责播放 DVD**。 > 同时,**两个线程间保留必要的交互**:例如,按下"暂停"键时中止播放。 > <br> ## 并发编程的适用场景 并发编程旨在让程序 "**同时**" 执行多个任务, - **计算密集型**:程序对 CPU 资源需求高,程序并发编程没有优势,反而会由于任务切换而导致效率降低。 - **I/O 密集型**:针对经常 "读写" or 访问数据库的程序,并发编程使得**当一个 I/O 操作阻塞时,CPU 可以切换到其他线程运行**,从而有效提升 CPU 利用率。 ## 并发相关术语 - **临界区**(critical section):访问 "**共享资源**" 的代码片段,资源通常指**一个变量或数据结构**。 - **竞态条件**(race condition):**多个线程同时访问==共享资源==时,操作顺序不确定,可能导致不可预期的行为**。 - **同步原语**(Synchronization Primitives):用于**协调多个线程间执行顺序、访问共享资源的一组==机制==**。 <br> # 并行模式 并发编程中的并行模式主要有两种: - **任务并行**(Task Parallelism):适用于**执行多个不同任务或工作流程**的情况 - 不同线程**同时执行多个不同任务**; - 将一个任务分解为多个**子任务**,**多个线程分别执行一个子任务**。 - **数据并行**(Data Parallelism):适用于**在大量数据上执行相同或相似操作**的情况 - 将数据划分成多个部分,然后**并行地对不同数据子集==执行相同的操作==**。 > [!example] 两种并行模式的示例 > - 任务并行示例:Web 服务器处理多个独立的客户端请求,图形引擎中的渲染和物理计算并行执行。 > - 数据并行示例:向量化操作、并行数组处理。 > > 实际应用中,任务并行和数据并行往往会结合使用,以充分利用多核处理器的计算能力。 > 例如,在一个大型科学计算项目中,可能**需要对一个大型数据集执行相同的数值分析(数据并行)**,**同时也需要执行日志记录、数据加载和结果验证等多种不同的任务**(任务并行)。 > <br><br> # 并发编程设计模式 两种最常用的**并发编程设计模式**,广泛用于**高性能服务器并发编程**中: - **半同步/半异步模式** - **Leader/Followers 模式** ## 半同步/半异步模式 该模式下,同时使用 "**同步线程**" 与 "**异步线程**": - **同步线程**:处理**业务逻辑**,例如计算密集型任务; - **异步线程**:处理 **I/O 事件**,例如网络连接、**读写请求**等。 **异步线程**监听到来自客户端的请求后,将其封装成**请求对象**推送到「**==请求队列==**」中,通知**同步线程**从任务队列中取出任务,进行处理。 > [!info] 并发中的 "同步" 与 "异步" > > 与 I/O 中的同步/异步概念不同,在并发中: > > - **同步**:程序**按照代码序列的顺序执行**。 > - **异步**:程序的执行需要**由系统事件驱动**。(例如调用 `epoll_wait` 阻塞的主线程是异步线程) > > ![[_attachment/02-开发笔记/05-操作系统/并发相关/并发与并行.assets/IMG-并发与并行-75602CFDD997872EAF0069C98018C483.png|522]] > <br> ## 半同步/半反应堆模式 **半同步/半反应堆模式**(half-sync/half-reactive),**半同步/半异步模式的一种变体**: - **异步线程**:只有一个,**由主线程(Reactor)充当**,负责监听 socket 上的事件; - **同步线程**:所有同步线程作为 "**工作线程**" 睡眠在请求队列上,当任务到来时,通过竞争(例如申请互斥锁)获取任务的接管权。 ![[_attachment/02-开发笔记/05-操作系统/并发相关/并发与并行.assets/IMG-并发与并行-4CAA0900869304411B10D897F84AA44C.png|599]] <br> ## Leader / Followers 模式 Leader / Follower 模式[^1](P134)模式下,由**一组线程协作处理 I/O 事件**,线程角色分为两种: - Leader 线程:负责**监听等待 I/O 事件**,**推选新的 Leader 线程**、**处理 I/O 事件**; - Follower 线程:等待成为新的 Leader 线程; 协作过程如下: - 程序**在任一时刻都仅有一个 Leader 线程**,其负责**监听 I/O 事件**,而**其他线程作为 Followers 休眠在线程池中==等待成为 Leader==**。 - **当前 Leader 线程**检测到 I/O 事件时,其需要首先**从线程池中 "推选" 出一个==新的 Leader 线程==** 负责继续监听,而后**其本身则转向处理 I/O 事件**。 - 新 Leader 监听等待新 I/O 事件,而原先的 Leader 则去处理 I/O 事件。 #### 主要组件 - 句柄集 - 线程集 - 事件处理器 - 具体的事件处理器 <br><br> # 参考资料 # Footnotes [^1]: 《Linux 高性能服务器编程》——游双