国际访客建议访问 Primers 编程伙伴 国际服 以获得更好的体验。 快速访问 Python 线程控制 线程同步

# Python 的线程同步

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

例如下面这段代码,两个线程分别将共享的 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-05-31 19:53:12 创建于: 2025-05-31 19:53:12