约 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