目录
1. 为什么要学习并发编程
学习并发编程可以提升系统性能和用户体验,还能帮助开发者理解底层原理,增强面试技术竞争力,并能更好地解决复杂的并发问题。
1.1 提升程序性能
多核CPU通过让程序同时执行多个任务,提高程序的执行效率。
例如:在多核处理器上,通过并行计算加速数据处理;在高并发场景下,支持大量用户同时访问系统。
1.2 提高系统的响应能力
并发编程可以使程序在处理任务时,快速响应用户的请求。
一个 Web 服务器可以同时处理多个客户端的请求,而不会因为单一任务的阻塞导致系统无响应。
提高用户体验,减少等待时间。
1.3 解决 IO 阻塞问题
在很多程序中,IO 操作(如文件读写、网络请求)可能会占用较长时间。
通过并发编程,可以在一个线程等待 IO 的同时,另一个线程继续执行其他任务。
实现异步处理,大大提升系统的吞吐量和效率。
常见应用场景
高并发请求处理:如电商秒杀、抢购等。
多任务处理:如批量任务分发和数据处理。
数据爬取与分析:分布式爬虫高效并发抓取。
游戏开发:多人在线游戏需要处理并发操作。
2. 什么是进程与线程
进程是系统资源分配的单位,适合独立任务;
线程是 CPU 调度的单位,适合高效并发。
学习和理解进程与线程,有助于我们选择合适的编程模型,设计高效稳定的程序。
2.1 什么是进程
2.1.1 定义
进程是一个正在执行的程序的实例,是操作系统分配资源和调度的基本单位。每个进程都有独立的地址空间,包括代码段、数据段和堆栈段。
2.1.2 特点
独立性:进程之间是独立的,互不影响,每个进程有自己的资源和内存空间。
资源占用:每个进程会分配一定的系统资源(如 CPU 时间、内存等)。
开销大:创建、销毁和切换进程的开销较大,切换时需要保存和恢复上下文。
通信复杂:不同进程之间通信需要借助进程间通信(IPC)机制,如管道、共享内存、消息队列等。
2.1.3 进程里面有什么
地址空间
| 项目 | 描述 |
|---|---|
| 代码段 | 包含程序的可执行代码只读,不允许修改 |
| 数据段 | 存储全局变量和静态变量 |
| 堆 | 动态分配内存,由程序在运行时使用malloc、new 等分配,需手动释放 |
| 栈 | 用于存储函数调用的局部变量、参数以及返回地址,由系统自动管理 |
进程控制块(PCB)
PCB 是操作系统管理进程的重要数据结构,包括以下内容:
| 项目 | 描述 |
|---|---|
| 进程 ID(PID) | 进程的唯一标识符 |
| 程序计数器(PC) | 指向下一条将被执行的指令的地址 |
| CPU 寄存器 | 保存进程运行时的上下文 |
| 进程状态 | 如运行中(Running)、就绪(Ready)、等待(Waiting)等 |
| 内存管理信息 | 如页面表或段表,描述进程的地址空间布局 |
| 文件描述符表 | 记录进程打开的文件或 IO 资源 |
| 优先级 | 用于调度器决定进程的执行顺序 |
| 父进程与子进程信息 | 支持进程的层次关系管理 |
系统资源
进程需要操作系统分配的资源来运行,主要包括:
- CPU 时间:由调度器分配给进程执行指令。
- 内存:用于存储进程的代码、数据和运行时状态。
- 文件:进程可能会打开文件,操作文件或设备。
- IO 设备:如键盘、鼠标、显示器等,支持进程与外部交互。
线程
一个进程至少包含一个线程(主线程),多个线程共享进程的以下资源:
- 地址空间(代码段、数据段、堆等)。
- 文件句柄。
- 信号和 IPC 资源。
信号量和同步机制
进程内部可能包含一些同步标识,用于线程或子进程间的协调。例如:
- 信号量(Semaphore)
- 互斥锁(Mutex)
- 条件变量(Condition Variable)
2.2 什么是线程
2.2.1 定义
线程是进程中的一个执行单元,是 CPU 调度的基本单位。一个进程可以包含多个线程,这些线程共享进程的地址空间和资源。
2.2.2 特点
共享性:同一进程内的线程共享内存、文件句柄等资源。
轻量级:相比进程,线程的创建和销毁开销更小,切换速度更快。
独立调度:线程是独立调度的,每个线程有自己的程序计数器、寄存器和栈。
线程安全问题:由于线程共享进程资源,可能出现线程竞争(如对同一变量的修改),需要使用同步机制(如锁)保证线程安全。
2.3 进程与线程的关系
一个进程可以包含多个线程:线程是进程的一部分,多个线程共享进程的内存空间。
线程必须依赖于进程:没有进程就没有线程,线程是进程的子集。
多进程与多线程的选择:如果任务之间独立性强且需要隔离资源,选择多进程。如果任务需要频繁通信且资源共享,选择多线程。
2.4 进程之间的通信
进程之间的通信(IPC, Inter-Process Communication)是操作系统提供的一种机制,用于让不同的进程相互传递数据或信号。由于每个进程有独立的地址空间,进程之间不能直接共享内存,因此需要借助操作系统提供的通信方式。
2.4.1 管道(Pipe)
管道是一种半双工通信机制,数据只能单向流动,一个进程写入数据,另一个进程读取数据。
适用于父子进程之间的通信,数据以字节流的形式传递,数据具有临时性,读取后会从管道中移除。
常见匿名管道与命名管道两种类型,前者只能在具有亲缘关系的进程间通信(如父进程和子进程),后者不需要进程间有亲缘关系即可通信。
2.4.2 消息队列(Message Queue)
消息队列是一种先进先出的通信机制,允许一个进程以消息为单位向消息队列中写入数据,另一个进程从队列中读取消息。
可以在无亲缘关系的进程之间通信,支持消息的优先级设置,比管道更灵活,支持随机读取指定的消息。
2.4.3 共享内存(Shared Memory)
共享内存是一种允许多个进程直接共享同一块内存区域的通信方式,是最快的 IPC 方法。
适合需要快速、大量传递数据的场景,不需要复制数据,直接访问共享区域,需要额外的同步机制(如信号量)来防止竞争条件。
2.4.4 信号(Signal)
信号是一种异步通信机制,用于通知进程发生了某种事件。
适合传递简单的控制信息(如中断、终止信号),不适合传递复杂数据,信号处理程序由操作系统调用。
2.4.5 信号量(Semaphore)
信号量是一种用于多进程同步的机制,通过控制访问共享资源的信号计数器,避免竞争条件。常用于进程间同步,而不是直接传递数据,通过加锁机制保护共享资源的访问。
2.4.6. 套接字(Socket)
套接字是一种支持跨网络通信的机制,同时也可以用于同一台主机上不同进程之间的通信。适合分布式进程间通信,可以使用多种协议(如 TCP、UDP),支持本地通信(Unix Domain Sockets)。
2.5 进程间通信方式对比
| 方式 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 管道 | 父子进程间简单数据传递 | 实现简单,操作方便 | 数据单向,亲缘关系进程限定 |
| 消息队列 | 无亲缘关系进程间的消息传递 | 消息有优先级,灵活 | 开销比管道大,复杂度较高 |
| 共享内存 | 大量数据快速传递 | 速度最快 | 需要同步机制,管理复杂 |
| 信号 | 异步事件通知 | 简单高效 | 数据量有限,仅适合控制信息 |
| 信号量 | 进程间同步 | 有效避免资源竞争 | 只同步,不传递数据 |
| 套接字 | 分布式或本地复杂通信 | 灵活强大,可跨主机通信 | 通信协议复杂,效率相对较低 |
| 文件 | 简单数据的持久化传递 | 实现简单,可持久化 | 速度较慢,适合低频通信 |
| 内存映射 | 高效的共享内存通信 | 快速,支持大数据传递 | 管理和同步复杂 |
2.6 cpu核心数与线程数
2.6.1 基本概念
核心数:CPU 内部的物理核心数,代表 CPU 同时执行任务的能力。每个核心相当于一个独立的处理单元。
线程数:系统可以并发运行的线程数量,CPU 的任务调度能力。
逻辑核心数:每个物理核心可以支持的逻辑线程数量。
2.6.2 核心数与线程数的关系
无超线程技术的 CPU:每个核心只能运行一个线程,核心数和线程数相等,4核CPU=4线程。
超线程技术的 CPU:每个核心可以运行 2 个线程,线程数 = 核心数 × 每核心支持的线程数,4核CPU=8线程。
2.6.3 线程数与核心数对比
线程数 ≤ 核心数
- 每个线程有独立的核心支持,无需上下文切换。
线程数 > 核心数
- 如果线程数超过核心数,操作系统通过时间片轮转机制在核心上调度多个线程。
- CPU 频繁进行上下文切换,可能会导致性能下降。
- 适用于 I/O 密集型任务(如网络请求处理),因为线程等待 I/O 的时间可以被其他线程利用。
线程数远远大于核心数
- 上下文切换开销急剧增加。
- CPU 缓存频繁失效(Cache Miss)。
- 系统资源(如内存)不足,可能导致程序性能严重下降或崩溃。
2.7 线程的上下文切换
线程的上下文切换是指当操作系统将 CPU 的执行权从一个线程切换到另一个线程时,需要保存当前线程的运行状态(上下文)并恢复目标线程的运行状态的过程。
2.7.1 上下文切换的过程
保存当前线程的状态:将当前线程的寄存器值、程序计数器等保存到其线程控制块中。
选择下一个线程:操作系统根据线程调度算法(如时间片轮转、优先级调度等)决定下一个要运行的线程。
恢复目标线程的状态:从目标线程的 TCB 中恢复寄存器值、程序计数器等,将其加载到 CPU
切换内存上下文:更新 CPU 的内存管理单元(MMU),切换到目标线程所在的进程地址空间
开始执行目标线程:CPU 从目标线程保存的程序计数器指向的位置继续执行
并发编程之线程与进程基础解析

被折叠的 条评论
为什么被折叠?



