Skip to content

第二章 多线程与多进程 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}")  # 输出: 500000

2.可重入锁(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
混合型任务多进程+异步充分利用系统资源

二、注意事项 ​

  1. 共享资源:多线程访问共享资源时需要加锁
  2. 进程通信:多进程间通信使用队列或管道
  3. 资源清理:确保进程/线程正确退出
  4. 异常处理:在子进程/线程中捕获异常