第二章 多线程与多进程 v1.0
Python提供了多线程和多进程两种并发编程方式,适用于不同的场景
第一部分 线程与进程基础
一、基本概念
1.进程
进程是程序的一次执行过程,是系统资源分配和调度的基本单位。每个进程都有自己独立的内存空间和系统资源。
2.线程
线程是进程中的一个执行单元,是CPU调度的基本单位。一个进程可以包含多个线程,它们共享进程的内存空间和资源。
3.区别
| 特性 | 进程 | 线程 |
|---|---|---|
| 资源分配 | 独立内存空间 | 共享进程内存 |
| 创建开销 | 较大 | 较小 |
| 通信方式 | IPC(管道、消息队列等) | 共享内存 |
| 稳定性 | 一个进程崩溃不影响其他进程 | 一个线程崩溃可能导致整个进程崩溃 |
二、Python中的GIL
GIL(Global Interpreter Lock)是CPython解释器中的全局解释器锁,它确保同一时刻只有一个线程执行Python字节码。
GIL的存在使得Python的多线程在CPU密集型任务中无法利用多核优势,但在IO密集型任务中仍然有效
第二部分 多线程编程
一、threading模块
Python的threading模块提供了创建和管理线程的接口。
1.创建线程
python
import threading
def task():
print(f"线程 {threading.current_thread().name} 正在执行")
# 创建线程
t = threading.Thread(target=task, name="MyThread")
# 启动线程
t.start()
# 等待线程结束
t.join()python
import threading
def task(num, name):
print(f"线程 {name}: 数字 {num}")
t = threading.Thread(target=task, args=(1, "Thread-1"))
t.start()
t.join()2.线程类
python
import threading
class MyThread(threading.Thread):
def __init__(self, name, delay):
super().__init__()
self.name = name
self.delay = delay
def run(self):
print(f"线程 {self.name} 开始")
import time
time.sleep(self.delay)
print(f"线程 {self.name} 结束")
t = MyThread("Worker", 2)
t.start()
t.join()二、线程同步
当多个线程访问共享资源时,需要使用同步机制避免竞态条件。
1.锁(Lock)
python
import threading
counter = 0
lock = threading.Lock()
def increment():
global counter
for _ in range(100000):
lock.acquire()
counter += 1
lock.release()
threads = []
for _ in range(5):
t = threading.Thread(target=increment)
threads.append(t)
t.start()
for t in threads:
t.join()
print(f"最终计数: {counter}") # 输出: 5000002.可重入锁(RLock)
python
import threading
rlock = threading.RLock()
def recursive_function(n):
rlock.acquire()
try:
if n > 0:
recursive_function(n - 1)
finally:
rlock.release()
t = threading.Thread(target=recursive_function, args=(5,))
t.start()
t.join()3.信号量(Semaphore)
python
import threading
import time
semaphore = threading.Semaphore(3) # 最多3个线程同时访问
def access_resource(thread_id):
semaphore.acquire()
try:
print(f"线程 {thread_id} 正在访问资源")
time.sleep(1)
finally:
semaphore.release()
print(f"线程 {thread_id} 释放资源")
threads = []
for i in range(5):
t = threading.Thread(target=access_resource, args=(i,))
threads.append(t)
t.start()
for t in threads:
t.join()三、线程间通信
1.队列(Queue)
python
import threading
import queue
q = queue.Queue()
def producer():
for i in range(5):
q.put(i)
print(f"生产: {i}")
import time
time.sleep(0.5)
def consumer():
while True:
item = q.get()
if item is None:
break
print(f"消费: {item}")
q.task_done()
t1 = threading.Thread(target=producer)
t2 = threading.Thread(target=consumer)
t1.start()
t2.start()
t1.join()
q.put(None) # 发送结束信号
t2.join()2.事件(Event)
python
import threading
import time
event = threading.Event()
def waiter():
print("等待者: 等待事件触发")
event.wait()
print("等待者: 事件已触发")
def setter():
print("设置者: 3秒后触发事件")
time.sleep(3)
event.set()
print("设置者: 事件已触发")
t1 = threading.Thread(target=waiter)
t2 = threading.Thread(target=setter)
t1.start()
t2.start()
t1.join()
t2.join()第三部分 多进程编程
一、multiprocessing模块
Python的multiprocessing模块提供了创建和管理进程的接口,可以绕过GIL的限制。
1.创建进程
python
from multiprocessing import Process
import os
def task():
print(f"进程 {os.getpid()} 正在执行")
if __name__ == '__main__':
p = Process(target=task)
p.start()
p.join()python
from multiprocessing import Process
import os
def task(num, name):
print(f"进程 {os.getpid()} - {name}: 数字 {num}")
if __name__ == '__main__':
p = Process(target=task, args=(1, "Worker"))
p.start()
p.join()2.进程类
python
from multiprocessing import Process
import os
class MyProcess(Process):
def __init__(self, name):
super().__init__()
self.name = name
def run(self):
print(f"进程 {self.name} (PID: {os.getpid()}) 正在执行")
if __name__ == '__main__':
p = MyProcess("Worker")
p.start()
p.join()二、进程间通信
1.队列(Queue)
python
from multiprocessing import Process, Queue
def producer(q):
for i in range(5):
q.put(i)
print(f"生产: {i}")
def consumer(q):
while True:
item = q.get()
if item is None:
break
print(f"消费: {item}")
if __name__ == '__main__':
q = Queue()
p1 = Process(target=producer, args=(q,))
p2 = Process(target=consumer, args=(q,))
p1.start()
p2.start()
p1.join()
q.put(None) # 发送结束信号
p2.join()2.管道(Pipe)
python
from multiprocessing import Process, Pipe
def sender(conn):
for i in range(5):
conn.send(i)
print(f"发送: {i}")
conn.close()
def receiver(conn):
while True:
try:
item = conn.recv()
print(f"接收: {item}")
except EOFError:
break
if __name__ == '__main__':
parent_conn, child_conn = Pipe()
p1 = Process(target=sender, args=(parent_conn,))
p2 = Process(target=receiver, args=(child_conn,))
p1.start()
p2.start()
p1.join()
p2.join()三、进程池
python
from multiprocessing import Pool
import os
def square(x):
print(f"进程 {os.getpid()} 计算 {x}^2")
return x * x
if __name__ == '__main__':
with Pool(4) as pool:
results = pool.map(square, range(10))
print(f"结果: {results}")第四部分 concurrent.futures模块
Python 3.2引入的concurrent.futures模块提供了更高级的并发接口。
一、线程池执行器
python
from concurrent.futures import ThreadPoolExecutor
import time
def task(n):
time.sleep(1)
return n * n
if __name__ == '__main__':
with ThreadPoolExecutor(max_workers=3) as executor:
futures = [executor.submit(task, i) for i in range(5)]
for future in futures:
result = future.result()
print(f"结果: {result}")二、进程池执行器
python
from concurrent.futures import ProcessPoolExecutor
import os
def task(n):
print(f"进程 {os.getpid()} 计算 {n}^2")
return n * n
if __name__ == '__main__':
with ProcessPoolExecutor(max_workers=3) as executor:
results = list(executor.map(task, range(5)))
print(f"结果: {results}")第五部分 最佳实践
一、选择多线程还是多进程
| 场景 | 推荐方式 | 原因 |
|---|---|---|
| IO密集型任务 | 多线程 | 线程创建开销小,IO等待时释放GIL |
| CPU密集型任务 | 多进程 | 绕过GIL,利用多核CPU |
| 混合型任务 | 多进程+异步 | 充分利用系统资源 |
二、注意事项
- 共享资源:多线程访问共享资源时需要加锁
- 进程通信:多进程间通信使用队列或管道
- 资源清理:确保进程/线程正确退出
- 异常处理:在子进程/线程中捕获异常