第111集:进程与线程概念
学习目标
- 理解进程和线程的基本概念
- 掌握进程与线程的区别与联系
- 了解并行与并发的概念
- 理解多进程与多线程的优缺点
- 掌握Python中进程与线程的基本使用方法
一、进程与线程的基本概念
1.1 什么是进程
进程(Process)是操作系统进行资源分配和调度的基本单位,是程序在执行过程中的实例。当我们运行一个程序时,操作系统会为其创建一个进程,并分配内存、CPU时间片等资源。
每个进程都有自己独立的内存空间、文件描述符和系统资源,进程之间相互隔离,不会直接影响对方。
进程的特点:
- 独立性:进程之间相互独立,拥有各自的资源
- 动态性:进程是程序的执行过程,有创建、执行、终止等状态变化
- 并发性:多个进程可以同时在系统中运行
- 异步性:进程的执行是异步的,不可预知的
1.2 什么是线程
线程(Thread)是进程中的一个执行单元,是CPU调度的最小单位。一个进程可以包含多个线程,这些线程共享进程的内存空间和系统资源。
线程有时也被称为"轻量级进程",因为线程之间的切换比进程之间的切换开销更小。
线程的特点:
- 共享性:同一进程内的线程共享进程的内存空间和资源
- 独立性:每个线程有自己的程序计数器、寄存器和栈
- 并发性:线程之间可以并发执行
- 开销小:线程创建和切换的开销比进程小
1.3 进程与线程的关系
┌─────────────────────────────────────────────────────────┐
│ 操作系统 │
├───────────────────────┬─────────────────────────────────┤
│ │ │
│ ┌─────────────────┐ │ ┌─────────────────┐ │
│ │ 进程1 │ │ │ 进程2 │ │
│ ├─────────────────┤ │ ├─────────────────┤ │
│ │ 独立内存空间 │ │ │ 独立内存空间 │ │
│ ├─────────────────┤ │ ├─────────────────┤ │
│ │ ┌───────────┐ │ │ │ ┌───────────┐ │ │
│ │ │ 线程1 │ │ │ │ │ 线程3 │ │ │
│ │ └───────────┘ │ │ │ └───────────┘ │ │
│ │ ┌───────────┐ │ │ │ ┌───────────┐ │ │
│ │ │ 线程2 │ │ │ │ │ 线程4 │ │ │
│ │ └───────────┘ │ │ │ └───────────┘ │ │
│ └─────────────────┘ │ └─────────────────┘ │
│ │ │
└───────────────────────┴─────────────────────────────────┘进程与线程的关系:
- 一个进程可以包含多个线程,但至少有一个线程(主线程)
- 线程是进程的一部分,共享进程的资源
- 进程之间相互独立,线程之间共享内存空间
- 进程切换比线程切换开销大
二、并行与并发
2.1 并发(Concurrency)
并发是指在同一时间间隔内,多个任务交替执行。从宏观上看,这些任务似乎是同时运行的,但从微观上看,它们是在CPU上交替执行的。
并发的实现依赖于操作系统的调度机制,通过时间片轮转等方式,让CPU在不同任务之间快速切换,从而给人一种同时执行的错觉。
2.2 并行(Parallelism)
并行是指在同一时刻,多个任务在不同的CPU核心上同时执行。并行需要多核CPU的支持,只有在多核环境下才能实现真正的并行。
2.3 并发与并行的区别
| 特性 | 并发(Concurrency) | 并行(Parallelism) |
|---|---|---|
| 执行方式 | 交替执行 | 同时执行 |
| CPU核心 | 单核或多核均可 | 必须多核 |
| 资源共享 | 可以共享资源 | 通常不共享或通过同步机制共享 |
| 目的 | 提高资源利用率 | 提高任务执行速度 |
| 实现难度 | 较低,依赖调度机制 | 较高,需要处理同步和通信 |
并发示意图:
时间 ────────────────────────────────────>
CPU1: [任务A][任务B][任务A][任务B][任务A]
并行示意图:
时间 ────────────────────────────────────>
CPU1: [任务A][任务A][任务A][任务A][任务A]
CPU2: [任务B][任务B][任务B][任务B][任务B]三、多进程与多线程
3.1 多进程(Multi-Process)
多进程是指在一个应用程序中同时创建多个进程来执行任务。每个进程有自己独立的内存空间和资源,进程之间通过IPC(Inter-Process Communication)机制进行通信。
多进程的优点:
- 稳定性高:一个进程崩溃不会影响其他进程
- 可以充分利用多核CPU
- 适合CPU密集型任务
多进程的缺点:
- 创建和切换开销大
- 资源占用多
- 进程间通信复杂
3.2 多线程(Multi-Thread)
多线程是指在一个进程中同时创建多个线程来执行任务。线程之间共享进程的内存空间和资源,通过共享内存进行通信。
多线程的优点:
- 创建和切换开销小
- 资源占用少
- 线程间通信简单
- 适合I/O密集型任务
多线程的缺点:
- 稳定性低:一个线程崩溃可能导致整个进程崩溃
- 存在线程安全问题
- Python中受GIL限制,CPU密集型任务性能提升不明显
3.3 多进程与多线程的选择
| 任务类型 | 推荐使用 | 原因 |
|---|---|---|
| CPU密集型 | 多进程 | 避免GIL限制,充分利用多核CPU |
| I/O密集型 | 多线程/异步IO | 减少等待时间,提高资源利用率 |
| 高稳定性要求 | 多进程 | 进程隔离,提高系统稳定性 |
| 低开销要求 | 多线程 | 线程创建和切换开销小 |
| 复杂数据共享 | 多线程 | 共享内存,通信简单 |
四、Python中的进程与线程
4.1 Python中的进程
Python提供了multiprocessing模块用于创建和管理进程。该模块支持跨平台,可以在Windows、Linux和macOS上使用。
基本使用示例:
import multiprocessing
import time
def worker(num):
"""进程执行的任务"""
print(f'进程 {num} 开始执行')
time.sleep(2)
print(f'进程 {num} 执行完成')
return num
if __name__ == '__main__':
# 创建进程池
with multiprocessing.Pool(processes=3) as pool:
# 提交任务
results = pool.map(worker, range(5))
print(f'所有进程执行完成,结果: {results}')4.2 Python中的线程
Python提供了threading模块用于创建和管理线程。该模块提供了丰富的线程操作接口。
基本使用示例:
import threading
import time
def worker(num):
"""线程执行的任务"""
print(f'线程 {num} 开始执行')
time.sleep(2)
print(f'线程 {num} 执行完成')
if __name__ == '__main__':
# 创建线程列表
threads = []
# 创建并启动线程
for i in range(5):
t = threading.Thread(target=worker, args=(i,))
threads.append(t)
t.start()
# 等待所有线程完成
for t in threads:
t.join()
print('所有线程执行完成')4.3 GIL(全局解释器锁)
GIL(Global Interpreter Lock)是Python解释器(如CPython)中的一个机制,它确保同一时刻只有一个线程在解释器中执行Python字节码。
GIL的存在主要是为了简化Python解释器的实现,尤其是内存管理部分。但GIL也带来了一些限制:
- 在多核CPU上,多线程的Python程序并不能充分利用多核优势
- CPU密集型任务在多线程环境下性能提升不明显,甚至可能下降
- I/O密集型任务受GIL影响较小,因为I/O操作会释放GIL
GIL对Python多线程的影响:
# CPU密集型任务示例
import threading
import time
def count(n):
while n > 0:
n -= 1
# 单线程执行
t1 = time.time()
count(100000000)
print(f'单线程执行时间: {time.time() - t1:.2f}秒')
# 多线程执行
thread1 = threading.Thread(target=count, args=(50000000,))
thread2 = threading.Thread(target=count, args=(50000000,))
t1 = time.time()
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print(f'多线程执行时间: {time.time() - t1:.2f}秒')在大多数情况下,由于GIL的存在,多线程执行CPU密集型任务的时间可能比单线程还要长。
五、进程与线程的生命周期
5.1 进程的生命周期
进程从创建到终止会经历以下几个状态:
- 创建状态(New):进程正在被创建
- 就绪状态(Ready):进程已创建完成,等待CPU调度
- 运行状态(Running):进程正在CPU上执行
- 阻塞状态(Blocked):进程等待某个事件完成(如I/O操作)
- 终止状态(Terminated):进程执行完成或被终止
5.2 线程的生命周期
线程的生命周期与进程类似,也包括以下几个状态:
- 新建状态(New):线程对象已创建,但尚未启动
- 就绪状态(Runnable):线程已启动,等待CPU调度
- 运行状态(Running):线程正在CPU上执行
- 阻塞状态(Blocked):线程等待某个事件完成
- 死亡状态(Dead):线程执行完成或被终止
六、进程间通信
由于进程之间相互隔离,需要通过特定的机制进行通信。Python提供了多种进程间通信方式:
6.1 队列(Queue)
队列是一种线程安全、进程安全的数据结构,可以用于进程间通信。
示例:
import multiprocessing
def producer(queue):
"""生产者进程"""
for i in range(5):
queue.put(i)
print(f'生产者生产了: {i}')
def consumer(queue):
"""消费者进程"""
while True:
item = queue.get()
if item is None: # 结束信号
break
print(f'消费者消费了: {item}')
if __name__ == '__main__':
# 创建队列
queue = multiprocessing.Queue()
# 创建生产者和消费者进程
p_producer = multiprocessing.Process(target=producer, args=(queue,))
p_consumer = multiprocessing.Process(target=consumer, args=(queue,))
# 启动进程
p_producer.start()
p_consumer.start()
# 等待生产者完成
p_producer.join()
# 发送结束信号
queue.put(None)
# 等待消费者完成
p_consumer.join()
print('所有进程执行完成')6.2 管道(Pipe)
管道是一种双向通信机制,可以在两个进程之间传递数据。
示例:
import multiprocessing
def sender(conn):
"""发送数据的进程"""
data = [1, 2, 3, 4, 5]
for item in data:
conn.send(item)
print(f'发送了: {item}')
conn.close()
def receiver(conn):
"""接收数据的进程"""
while True:
try:
item = conn.recv()
print(f'接收了: {item}')
except EOFError:
break
if __name__ == '__main__':
# 创建管道
parent_conn, child_conn = multiprocessing.Pipe()
# 创建发送者和接收者进程
p_sender = multiprocessing.Process(target=sender, args=(child_conn,))
p_receiver = multiprocessing.Process(target=receiver, args=(parent_conn,))
# 启动进程
p_sender.start()
p_receiver.start()
# 等待进程完成
p_sender.join()
p_receiver.join()
print('所有进程执行完成')6.3 共享内存(Shared Memory)
共享内存允许多个进程访问同一块内存区域,是一种高效的进程间通信方式。
示例:
import multiprocessing
def update_shared_value(shared_value):
"""更新共享值"""
for i in range(10):
shared_value.value += 1
print(f'更新后的值: {shared_value.value}')
if __name__ == '__main__':
# 创建共享值
shared_value = multiprocessing.Value('i', 0) # 'i'表示整数类型
# 创建两个进程
p1 = multiprocessing.Process(target=update_shared_value, args=(shared_value,))
p2 = multiprocessing.Process(target=update_shared_value, args=(shared_value,))
# 启动进程
p1.start()
p2.start()
# 等待进程完成
p1.join()
p2.join()
print(f'最终值: {shared_value.value}')七、线程同步与互斥
由于线程共享进程的内存空间,当多个线程同时访问共享资源时,可能会导致数据不一致的问题,这称为线程安全问题。
7.1 线程安全问题
示例:
import threading
# 共享变量
counter = 0
def increment():
global counter
for _ in range(100000):
counter += 1
# 创建两个线程
thread1 = threading.Thread(target=increment)
thread2 = threading.Thread(target=increment)
# 启动线程
thread1.start()
thread2.start()
# 等待线程完成
thread1.join()
thread2.join()
print(f'最终计数器值: {counter}') # 预期值为200000,但实际可能小于这个值问题分析:counter += 1看似是一个原子操作,但实际上它包含三个步骤:
- 读取
counter的当前值 - 将值加1
- 将新值写回
counter
当两个线程同时执行这个操作时,可能会出现以下情况:
- 线程1读取
counter的值为100 - 线程2也读取
counter的值为100 - 线程1将值加1得到101,并写回
counter - 线程2也将值加1得到101,并写回
counter
最终counter的值为101,而不是预期的102。
7.2 锁机制
为了解决线程安全问题,Python提供了锁机制。锁可以确保在同一时刻只有一个线程可以访问共享资源。
示例:
import threading
# 共享变量
counter = 0
# 创建锁
lock = threading.Lock()
def increment():
global counter
for _ in range(100000):
with lock: # 获取锁
counter += 1
# 自动释放锁
# 创建两个线程
thread1 = threading.Thread(target=increment)
thread2 = threading.Thread(target=increment)
# 启动线程
thread1.start()
thread2.start()
# 等待线程完成
thread1.join()
thread2.join()
print(f'最终计数器值: {counter}') # 预期值为200000使用锁机制可以确保counter += 1操作的原子性,从而解决线程安全问题。
八、总结与练习
8.1 总结
- 进程是操作系统资源分配的基本单位,线程是CPU调度的最小单位
- 并发是指任务交替执行,并行是指任务同时执行
- 多进程适合CPU密集型任务,多线程适合I/O密集型任务
- Python中由于GIL的存在,多线程在CPU密集型任务上性能提升不明显
- 进程间通信可以通过队列、管道、共享内存等方式实现
- 线程安全问题可以通过锁机制解决
8.2 练习
基础练习:
- 编写一个多进程程序,计算1到100000000的和
- 编写一个多线程程序,模拟下载多个文件
进阶练习:
- 使用队列实现生产者-消费者模式
- 使用共享内存实现两个进程之间的通信
- 实现一个线程安全的计数器
思考问题:
- 为什么Python中的多线程在CPU密集型任务上性能不好?
- 进程与线程的主要区别是什么?
- 什么是线程安全?如何保证线程安全?
九、扩展阅读
Python官方文档:
- multiprocessing模块:https://docs.python.org/3/library/multiprocessing.html
- threading模块:https://docs.python.org/3/library/threading.html
推荐书籍:
- 《Python并行编程手册》
- 《Python Cookbook》(第3版)第12章:并发编程
在线资源:
下集预告:第112集将学习线程的创建与启动,包括如何创建线程、启动线程、传递参数等内容。