Python 教程 / IO编程 / 进程和线程

进程和线程 (Process and Thread)

并发编程是现代软件开发的重要技能。本章将学习Python中的多进程多线程编程,理解它们的区别、适用场景以及如何避免并发问题。

进程 vs 线程

┌─────────────────────────────────────────────────────────────────┐
│                    进程 vs 线程 对比                               │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   【进程 (Process)】                                            │
│   ┌─────────────────────────────────────────────────────────┐   │
│   │  进程1              进程2              进程3            │   │
│   │  ┌─────────┐       ┌─────────┐       ┌─────────┐       │   │
│   │  │ 代码    │       │ 代码    │       │ 代码    │       │   │
│   │  │ 数据    │       │ 数据    │       │ 数据    │       │   │
│   │  │ 资源    │       │ 资源    │       │ 资源    │       │   │
│   │  └─────────┘       └─────────┘       └─────────┘       │   │
│   │       │                │                │               │   │
│   │       └────────────────┴────────────────┘               │   │
│   │                    操作系统                              │   │
│   └─────────────────────────────────────────────────────────┘   │
│   特点:独立内存空间,进程间隔离,启动开销大                     │
│                                                                 │
│   【线程 (Thread)】                                              │
│   ┌─────────────────────────────────────────────────────────┐   │
│   │  进程                                                      │   │
│   │  ┌──────────────────────────────────────────────────┐    │   │
│   │  │  线程1      线程2      线程3      主线程         │    │   │
│   │  │  ┌────┐    ┌────┐    ┌────┐    ┌────┐          │    │   │
│   │  │  │堆栈│    │堆栈│    │堆栈│    │堆栈│          │    │   │
│   │  │  └────┘    └────┘    └────┘    └────┘          │    │   │
│   │  └──────────────────────────────────────────────────┘    │   │
│   │         共用代码、数据、资源、文件描述符                   │   │
│   └─────────────────────────────────────────────────────────┘   │
│   特点:共享进程资源,启动开销小,需要同步                       │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
                         
特性 进程 线程
内存空间 独立 共享进程内存
创建开销
通信方式 Pipe, Queue, 共享内存 直接共享变量(需同步)
稳定性 高(隔离) 低(共享资源)
适用场景 CPU密集型、并行计算 IO密集型、异步操作
Python GIL 限制
由于Python的GIL(全局解释器锁),多线程不能真正并行执行CPU密集型任务。
对于CPU密集型任务,应使用多进程;对于IO密集型任务,多线程仍然有效。

threading 多线程

1. 创建线程

Python
import threading
import time

# 方式1: 创建 Thread 对象
def task(name, seconds):
    print(f"线程 {name} 开始")
    time.sleep(seconds)
    print(f"线程 {name} 结束")

# 创建线程
t1 = threading.Thread(target=task, args=("A", 1))
t2 = threading.Thread(target=task, args=("B", 0.5))

# 启动线程
t1.start()
t2.start()

# 等待线程结束
t1.join()
t2.join()

print("所有线程完成") 

2. 继承 Thread 类

Python
class MyThread(threading.Thread):
    def __init__(self, name, delay):
        super().__init__()
        self.name = name
        self.delay = delay

    def run(self):
        print(f"线程 {self.name} 开始执行")
        time.sleep(self.delay)
        print(f"线程 {self.name} 执行完成")

threads = [
    MyThread("Thread-1", 1),
    MyThread("Thread-2", 0.5),
]

for t in threads:
    t.start()

for t in threads:
    t.join()

print("主线程继续执行") 

3. 线程同步 - 锁

Python
import threading

# 共享资源
counter = 0
counter_lock = threading.Lock()

def increment():
    global counter
    for _ in range(100000):
        with counter_lock:   # 获取锁
            counter += 1

# 创建多个线程
threads = [threading.Thread(target=increment) for _ in range(5)]

for t in threads:
    t.start()

for t in threads:
    t.join()

print(f"计数器最终值: {counter}") 

ThreadLocal

ThreadLocal 为每个线程提供独立的变量副本,避免线程间的变量冲突。

┌─────────────────────────────────────────────────────────────────┐
│                      ThreadLocal 工作原理                         │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   local_data = threading.local()                                │
│                                                                 │
│   线程1                    线程2                    线程3      │
│   ┌────────────┐          ┌────────────┐          ┌────────────┐│
│   │ local_data │          │ local_data │          │ local_data ││
│   │   .x = 1   │          │   .x = 2   │          │   .x = 3   ││
│   │   .y = "A" │          │   .y = "B" │          │   .y = "C" ││
│   └────────────┘          └────────────┘          └────────────┘│
│        ▲                       ▲                       ▲        │
│        │                       │                       │        │
│        └───────────────────────┴───────────────────────┘        │
│                          ThreadLocal                            │
│                    (每个线程看到的是自己的副本)                   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
                         
Python
import threading

# ThreadLocal 对象
local_data = threading.local()

def process():
    # 每个线程独立设置自己的数据
    local_data.value = threading.current_thread().name
    print(f"线程 {threading.current_thread().name}: local_data.value = {local_data.value}")

# 创建多个线程
threads = [threading.Thread(target=process) for _ in range(3)]

for t in threads:
    t.start()
for t in threads:
    t.join() 

multiprocessing 多进程

1. 基本用法

Python
from multiprocessing import Process, Pool
import os

def worker(name):
    print(f"进程 {name} (PID: {os.getpid()}) 开始")
    print(f"进程 {name} 结束")

# 创建多个进程
if __name__ == "__main__":
    processes = []
    for i in range(3):
        p = Process(target=worker, args=(i,))
        processes.append(p)
        p.start()

    for p in processes:
        p.join()

    print(f"主进程 PID: {os.getpid()}") 

2. 进程池 Pool

Python
from multiprocessing import Pool
import time

def square(n):
    return n * n

if __name__ == "__main__":
    # 创建进程池(4个工作进程)
    with Pool(processes=4) as pool:
        # map - 并行执行
        results = pool.map(square, [1, 2, 3, 4, 5])
        print(f"map 结果: {results}")

        # apply_async - 异步执行
        result = pool.apply_async(square, (10,))
        print(f"apply_async 结果: {result.get()}")

        # 批量异步
        results = pool.map_async(square, [6, 7, 8])
        print(f"map_async 结果: {results.get()}") 

3. 进程间通信

Python
from multiprocessing import Process, Queue

def producer(queue):
    for i in range(5):
        queue.put(i)
    queue.put(None)   # 发送结束信号

def consumer(queue):
    while True:
        item = queue.get()
        if item is None:
            break
        print(f"消费: {item}")

if __name__ == "__main__":
    queue = Queue()
    p1 = Process(target=producer, args=(queue,))
    p2 = Process(target=consumer, args=(queue,))
    p1.start()
    p2.start()
    p1.join()
    p2.join() 

知识测验

问题1:Python多线程不能真正并行执行的原因是?

问题2:ThreadLocal的作用是什么?

课后练习

✏️ 练习1:多线程下载器

实现一个简单的多线程任务模拟:

  • 创建多个线程,每个线程下载不同的"文件"
  • 使用ThreadLocal存储每个线程的下载进度
  • 使用锁保护共享的完成计数
  • 等待所有线程完成后输出总进度
✏️ 练习2:进程池并行计算

使用进程池进行并行计算:

  • 创建一个计算密集型函数(如求素数)
  • 使用进程池并行计算多个范围内的素数
  • 对比串行和并行的执行时间
🐍 Python 在线代码编辑器
📝 运行结果