线程锁这个概念在多线程编程中经常被提到,但很多初学者会觉得它有点抽象。简单来说,线程锁是一个机制,用来解决多线程环境下的资源竞争问题。
它的作用就是控制线程对共享资源的访问,防止出现数据不一致的问题。今天我们就通过一个例子来讲清楚线程锁的原理和使用方法。
首先,为什么需要线程锁?
假设你有一个变量 n
,初始值是 0。两个线程同时对这个变量进行加 1 操作。正常情况下,你希望 n
最终的值是 2,对吧?但因为线程的操作不是原子性的(即每一步操作可能被打断),可能发生这样的情况:
线程 A 读取到 n = 0
,准备执行n = n + 1
。在线程 A 还没来得及写回结果时,线程 B 抢占了执行权,也读取到 n = 0
,并准备执行n = n + 1
。线程 B 先写回了结果,将 n
设置为 1。线程 A 恢复执行,也将 n
设置为 1。
结果,n
被两个线程加了两次,但最终值却是 1,而不是预期的 2。这种情况被称为数据竞争,会导致程序出现不可预料的错误。
为了解决这个问题,我们引入线程锁。锁的作用是:当一个线程对共享资源进行操作时,其它线程必须等到该线程释放锁后,才能访问这个资源。这样可以避免多个线程同时修改资源导致的冲突。
接下来,我们通过代码演示如何使用线程锁:
from threading import Thread, Lock
from time import sleep
import random
# 创建一个全局锁对象
lock = Lock()
# 共享资源
n = 0
def increment():
global n
# 加锁,确保同一时间只有一个线程能执行下面的代码
lock.acquire()
try:
local_value = n
sleep(random.uniform(0.1, 0.5)) # 模拟一些耗时操作
n = local_value + 1
print(f"{Thread.name} incremented n to {n}")
finally:
# 无论是否发生异常,都需要释放锁
lock.release()
# 创建多个线程同时操作共享资源
threads = []
for _ in range(5):
t = Thread(target=increment)
threads.append(t)
t.start()
# 等待所有线程完成
for t in threads:
t.join()
print(f"Final value of n: {n}")
代码解析
锁的创建:我们使用 Lock()
函数创建了一个锁对象。加锁:在修改共享资源之前,调用 lock.acquire()
来获取锁。只有获取到锁的线程才能继续执行。释放锁:在操作完成后,无论是否发生异常,都需要调用 lock.release()
来释放锁。这里我们使用了try-finally
来确保锁一定会被释放。线程安全:由于每个线程必须等待锁被释放后才能修改 n
,所以n
的最终值一定是正确的。
运行这段代码,你会发现,最终 n
的值总是等于线程的数量(这里是 5)。如果去掉锁,可能会导致错误的结果。
最后,我们来看一道面试题:线程锁的作用是什么?如何在 Python 中使用线程锁?
最佳回答:线程锁的作用是确保多个线程在访问共享资源时不会引起数据竞争问题,从而保证数据的一致性。
在 Python 中,可以通过 threading.Lock
创建一个锁对象,并使用 lock.acquire()
和 lock.release()
方法来加锁和解锁。加锁后,其他线程在调用 lock.acquire()
时会被阻塞,直到锁被释放。
from threading import Thread, Lock
lock = Lock()
shared_resource = 0
def critical_section():
global shared_resource
lock.acquire()
try:
shared_resource += 1
finally:
lock.release()
threads = [Thread(target=critical_section) for _ in range(10)]
for t in threads:
t.start()
for t in threads:
t.join()
print("Final value:", shared_resource
对编程、职场感兴趣的同学,大家可以联系我微信:golang404,拉你进入“程序员交流群”。
虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》。