• 预备
  • 基础语法
  • 容器类型
  • 函数
  • 面向对象
  • 输入输出
  • 进程控制
  • 线程控制
  • 正则表达式
  • 网络编程
  • 图形界面
  • 常见问题
  • API 帮助手册

  • 设置

2688

13 分钟

#线程同步

同一个进程中的不同线程共享内存空间和资源,当它们操作共享资源时会产生竞争。

例如下面这段代码,两个线程分别将共享的 counter 执行了 10 次加 1,结果本应该是 20,但实际上结果是随机的。

from threading import Thread import time # 计数器 class Counter: def __init__(self): self.__value = 0 # 计数加一 def increase(self): value = self.__value + 1 for _ in range(300000): # 延长执行时间,提高出错的概率,便于演示 pass self.__value = value # 读取计数 def value(self): return self.__value # 全局变量 counter = Counter() # 作为线程的入口函数 def worker(): time.sleep(1) global counter for _ in range(10): counter.increase() # 修改全局变量 t1 = Thread(target=worker) # 创建线程 t2 = Thread(target=worker) t1.start() # 启动线程 t2.start() t1.join() # 等待线程结束 t2.join() print(counter.value()) # 查看最终结果

多次运行,可以看见不同的结果:

$ ./main.py 12 $ ./main.py 19 $ ./main.py 11 $ ./main.py 10 $ ./main.py 17

这是因为可能产生了这样的流程:

  • 线程一读取到的 message 的值为 3,发生线程调度
  • 线程二读取到的 message 的值为 3
  • 线程二将 message 的值设为 3 + 1,发生线程调度
  • 线程一将 message 的值设为 3 + 1

为了让程序的行为符合预期,在操作共享资源时,需要进行线程同步。

#互斥锁

互斥锁是最简单的线程同步方案,一个线程加锁后,另一个线程尝试加锁时会阻塞,直到之前的线程解锁。

在 Python 中使用 threading 模块的 Lock 类创建互斥锁,通过 acquire 方法加锁,release方法解锁。

from threading import Thread, Lock import time # 计数器 class Counter: def __init__(self): self.__value = 0 self.__lock = Lock() # 计数加一 def increase(self): self.__lock.acquire() # 加锁,另一个线程在解锁前必须在此等待 value = self.__value + 1 for _ in range(300000): # 延长执行时间,提高出错的概率,便于演示 pass self.__value = value self.__lock.release() # 解锁 # 读取计数 def value(self): self.__lock.acquire() # 加锁,另一个线程在解锁前必须在此等待 return self.__value self.__lock.release() # 解锁 # 全局变量 counter = Counter() # 作为线程的入口函数 def worker(): time.sleep(1) global counter for _ in range(10): counter.increase() # 修改全局变量 t1 = Thread(target=worker) # 创建线程 t2 = Thread(target=worker) t1.start() # 启动线程 t2.start() t1.join() # 等待线程结束 t2.join() print(counter.value()) # 查看最终结果

Lock 拥有 __enter____exit__ 方法,分别进行加锁和解锁,因此可以使用 with 语句。例如:

# 计数加一 def increase(self): with self.__lock: value = self.__value + 1 for _ in range(300000): # 延长执行时间,提高出错的概率,便于演示 pass self.__value = value

创建于 2025/5/12 22:01:32

更新于 2025/5/13 15:50:52