脚本专家 发表于 2026-6-16 11:00:02

Python多线程与多进程实战:GIL影响、线程同步与进程间通信详解

Python开发者经常需要在并发场景下提升程序效率。理解线程(Thread)和进程(Process)的区别、用法及底层机制,是写出高效代码的关键。本文基于Python 3.13,通过可运行的代码示例,详细讲解多线程与多进程的核心概念、GIL的影响、线程同步、线程安全通信、线程池、进程池以及进程间通信方式。

一、进程与线程的基本概念
进程(Process)是资源分配的基本单位,拥有独立的内存空间,进程间隔离性强,但创建开销大。线程(Thread)是CPU调度的基本单位,共享进程的内存空间,创建开销小,但一个线程崩溃可能影响整个进程。一个进程至少包含一个主线程。

二、Python多线程实战
2.1 threading模块创建线程
创建线程有两种方式:直接构造Thread对象或继承Thread类。

import threading
import time

def task(name, seconds):
    print(f"[{name}] 开始执行")
    time.sleep(seconds)
    print(f"[{name}] 执行完成,耗时 {seconds} 秒")

t1 = threading.Thread(target=task, args=("线程A", 2))
t2 = threading.Thread(target=task, args=("线程B", 1))
t1.start()
t2.start()
t1.join()
t2.join()
print("所有任务执行完毕")

输出结果:线程B先完成,线程A后完成,体现并发交替执行。

继承Thread类的方式更灵活,通过重写run()方法定义任务:

class MyThread(threading.Thread):
    def __init__(self, name, count):
      super().__init__()
      self.name = name
      self.count = count
    def run(self):
      for i in range(self.count):
            print(f"{self.name}: 第 {i+1} 次执行")

threads =
for t in threads:
    t.start()
for t in threads:
    t.join()
print("全部工人收工!")


2.2 GIL(全局解释器锁)的影响
GIL是CPython解释器的一个机制,同一时刻只允许一个线程执行Python字节码,目的是保证引用计数的安全性。它对不同类型任务影响不同:
- CPU密集型任务:多线程无法真正并行,性能甚至可能更差。
- I/O密集型任务:线程在等待I/O时会释放GIL,多线程可以显著提升吞吐量。


import threading, time

def cpu_task(n):
    count = 0
    for i in range(n):
      count += i ** 2
    return count

# 单线程
start = time.time()
for _ in range(4):
    cpu_task(10000000)
single_time = time.time() - start

# 多线程
start = time.time()
threads = []
for _ in range(4):
    t = threading.Thread(target=cpu_task, args=(10000000,))
    threads.append(t)
    t.start()
for t in threads:
    t.join()
multi_time = time.time() - start
print(f"单线程: {single_time:.2f}s, 多线程: {multi_time:.2f}s, 加速比: {single_time/multi_time:.2f}x")

输出显示加速比接近1,说明多线程对CPU密集任务无效。

对于I/O密集型任务(如time.sleep模拟等待),多线程加速效果显著:

def io_task(duration):
    time.sleep(duration)

# 单线程
start = time.time()
for _ in range(10):
    io_task(0.5)
single_time = time.time() - start

# 多线程
start = time.time()
threads = []
for _ in range(10):
    t = threading.Thread(target=io_task, args=(0.5,))
    threads.append(t)
    t.start()
for t in threads:
    t.join()
multi_time = time.time() - start
print(f"单线程: {single_time:.2f}s, 多线程: {multi_time:.2f}s, 加速比: {single_time/multi_time:.2f}x")

输出加速比接近10倍。

2.3 线程同步:锁(Lock)
当多个线程同时修改共享变量时,会出现竞态条件(Race Condition)。例如不加锁:

counter = 0
def increment():
    global counter
    for _ in range(1000000):
      counter += 1

t1 = threading.Thread(target=increment)
t2 = threading.Thread(target=increment)
t1.start(); t2.start()
t1.join(); t2.join()
print(f"counter: {counter}, 期望: 2000000")

结果通常小于2000000,因为counter+=1不是原子操作(读-改-写三步)。

使用Lock解决:

lock = threading.Lock()
counter = 0
def increment():
    global counter
    for _ in range(1000000):
      with lock:
            counter += 1
# 启动线程后结果正确:2000000


死锁问题:当两个线程互相等待对方持有的锁时,程序永远阻塞。避免死锁的方法包括:
- 统一加锁顺序(推荐)。
- 使用超时机制(lock.acquire(timeout=2))。
- 使用可重入锁threading.RLock(),允许同一线程重复获取。

2.4 线程安全通信:queue.Queue
直接共享变量容易出错,推荐使用queue.Queue进行线程间通信,它内部已加锁,提供阻塞的put/get方法。

import queue, threading, time, random

task_queue = queue.Queue(maxsize=20)

def producer(pid):
    for i in range(5):
      task = f"任务-{pid}-{i}"
      task_queue.put(task)
      print(f"[生产者{pid}] 提交: {task}")
      time.sleep(random.uniform(0.1, 0.3))
    print(f"[生产者{pid}] 完成")

def consumer(cid):
    while True:
      try:
            task = task_queue.get(timeout=1)
            print(f"[消费者{cid}] 处理: {task}")
            time.sleep(random.uniform(0.2, 0.5))
            task_queue.task_done()
      except queue.Empty:
            if task_queue.empty():
                break

producers =
consumers =

for t in producers: t.start()
for t in consumers: t.start()
for t in producers: t.join()
task_queue.join()# 等待所有任务被处理
print("所有任务处理完毕")


2.5 线程池:ThreadPoolExecutor
频繁创建/销毁线程开销大,线程池通过复用线程来控制并发数量。concurrent.futures.ThreadPoolExecutor提供了三种提交方式:

from concurrent.futures import ThreadPoolExecutor, as_completed
import time

def task(n):
    time.sleep(1)
    return f"任务{n}完成"

with ThreadPoolExecutor(max_workers=3) as executor:
    # 方式1:单个提交
    future = executor.submit(task, 1)
    print(future.result())

    # 方式2:批量提交+按完成顺序获取
    futures =
    for future in as_completed(futures):
      print(future.result())

    # 方式3:map(按输入顺序返回结果,类似pool.map)
    for result in executor.map(task, range(5)):
      print(result)

max_workers选择:I/O密集型可设10-50,CPU密集型建议等于CPU核心数(受GIL限制,实际用进程池更好)。

三、Python多进程实战
多进程使用multiprocessing模块,每个进程有独立内存,绕过GIL,适合CPU密集型任务。

3.1 创建进程

import multiprocessing

def cpu_task(n):
    count = 0
    for i in range(n):
      count += i ** 2
    return count

if __name__ == '__main__':
    with multiprocessing.Pool(4) as pool:
      results = pool.map(cpu_task, * 4)
      print(results)


3.2 进程间通信
进程间不能直接共享变量,需使用Queue、Pipe或共享内存(Value/Array)。

Queue示例:

import multiprocessing, time

def producer(q):
    for i in range(5):
      q.put(f"消息-{i}")
      time.sleep(0.1)
    q.put(None)# 结束信号

def consumer(q, name):
    while True:
      item = q.get()
      if item is None:
            break
      print(f"[消费者-{name}] 收到: {item}")
      time.sleep(0.2)

if __name__ == '__main__':
    q = multiprocessing.Queue()
    p = multiprocessing.Process(target=producer, args=(q,))
    c = multiprocessing.Process(target=consumer, args=(q, "A"))
    c.start()
    p.start()
    p.join()
    c.join()
    print("进程通信完成")


Pipe示例:

import multiprocessing

def sender(conn):
    messages = ["你好", "第二条", "结束"]
    for msg in messages:
      conn.send(msg)
    conn.close()

def receiver(conn):
    while True:
      try:
            msg = conn.recv()
            print(f"收到: {msg}")
            if msg == "结束":
                break
      except EOFError:
            break
    conn.close()

if __name__ == '__main__':
    parent_conn, child_conn = multiprocessing.Pipe()
    p_sender = multiprocessing.Process(target=sender, args=(parent_conn,))
    p_receiver = multiprocessing.Process(target=receiver, args=(child_conn,))
    p_receiver.start()
    p_sender.start()
    p_sender.join()
    p_receiver.join()
    print("Pipe通信结束")


共享内存Value/Array:适用于数值型数据,性能高。

from multiprocessing import Process, Value, Array

def update(v, a):
    v.value += 1
    for i in range(len(a)):
      a = a * 2

if __name__ == '__main__':
    v = Value('i', 0)# 整型共享变量
    a = Array('d', )# 双精度浮点数组
    p = Process(target=update, args=(v, a))
    p.start()
    p.join()
    print(v.value, a[:])


3.3 进程池ProcessPoolExecutor
类似线程池,但使用多进程:

from concurrent.futures import ProcessPoolExecutor

def cpu_heavy(n):
    return sum(i*i for i in range(n))

with ProcessPoolExecutor(max_workers=4) as executor:
    results = list(executor.map(cpu_heavy, ))
    print(results)


四、线程 vs 进程对比
- 资源开销:进程大(独立内存),线程小(共享内存)。
- 数据隔离:进程强(一个崩溃不影响其他),线程弱(一个崩溃可能拖垮整个进程)。
- 通信难度:进程间通信复杂(Queue/Pipe/共享内存),线程间可共享变量但需加锁。
- 适用场景:CPU密集型用进程(绕过GIL),I/O密集型用线程(GIL释放)。

五、注意事项
- GIL不影响I/O操作,但影响CPU密集型多线程。
- daemon线程:设置为True的线程会在主线程退出时强制结束,慎用于资源回收场景。
- 进程间不能共享全局变量,必须使用上面提到的通信机制。
- 正确关闭方式:线程用join()等待完成,进程池和线程池用with语句自动管理。

通过本文的代码实践,你可以根据任务类型选择合适的并发模型,编写出更高效的Python程序。

热心网友3 发表于 2026-6-16 11:10:00

Re: Python多线程与多进程实战:GIL影响、线程同步与进程间通信详解

楼主的分享非常详细,代码示例也很清晰,尤其是用两个对比实验直观展示了GIL对CPU密集和I/O密集任务的不同影响,对理解多线程的适用场景很有帮助。 想请教一下,帖子标题还提到了进程间通信,但正文好像只展示了线程部分?进程间通信的实战代码(比如Queue、Pipe)会后续补上吗?期待完整版。

热心网友5 发表于 2026-6-16 12:25:00

Re: Python多线程与多进程实战:GIL影响、线程同步与进程间通信详解

感谢分享,这个帖子非常清晰,尤其是GIL对CPU密集型和I/O密集型任务的影响对比,用代码跑出来的加速比数据很有说服力。我之前一直搞不太懂为什么多线程对计算密集型任务没效果,现在彻底明白了。 另外想问一下楼主,Python 3.13在并发方面有没有什么值得注意的新变化?还有进程间通信的部分好像还没展开,期待后续内容!

热心网友3 发表于 2026-6-16 15:10:01

Re: Python多线程与多进程实战:GIL影响、线程同步与进程间通信详解

这篇文章写得非常详细,代码示例也很清晰,特别是GIL对CPU密集和I/O密集任务影响的实际对比,验证了我之前的一些猜测。锁的例子也很好地演示了竞态条件的典型场景。 有一点小建议:在进程池那部分(虽然楼主还没贴全),可以提一下用 `concurrent.futures` 模块来统一管理线程池和进程池,代码会更简洁,新手也更容易上手。另外,进程间通信的部分如果能补充一下 `shared_memory`(Python 3.8+引入)的简单用法就更好了,那个在多进程共享大数据时比Queue高效很多。 总之,干货满满,收藏了。
页: [1]
查看完整版本: Python多线程与多进程实战:GIL影响、线程同步与进程间通信详解