什么是锁?
在多线程编程中,锁是一种机制,用来确保某些代码块在同一时间只能被一个线程执行。想象一下,你和你的朋友们都想同时进入一个只有一把椅子的房间。为了避免混乱,你们需要一个锁来控制进入的顺序。
普通锁(Lock)
普通锁就像是一个简单的门锁。你拿到钥匙(获取锁),进了房间(执行代码),然后记得把钥匙放回去(释放锁),这样别人才能进来。
让我们看看Python中的普通锁是如何工作的:
import threading
lock = threading.Lock()
def critical_section():
with lock:
print(f"{threading.current_thread().name} has entered the critical section")
# 模拟一些工作
import time
time.sleep(1)
print(f"{threading.current_thread().name} is leaving the critical section")
threads = []
for i in range(3):
thread = threading.Thread(target=critical_section)
threads.append(thread)
thread.start()
for thread in threads:
thread.join()
在这个例子中,我们创建了一个普通锁,并在critical_section函数中使用它。每个线程在进入关键区之前都会获取锁,并在离开时释放锁。这样可以确保同一时间只有一个线程在执行关键区的代码。
普通锁的死锁场景
普通锁虽然简单高效,但在某些情况下会导致死锁。让我们看看一个死锁的例子:
import threading
lock = threading.Lock()
def deadlock_function():
lock.acquire()
print(f"{threading.current_thread().name} has acquired the lock")
# 尝试再次获取同一把锁,导致死锁
lock.acquire()
print(f"{threading.current_thread().name} has acquired the lock again")
lock.release()
lock.release()
thread = threading.Thread(target=deadlock_function)
thread.start()
thread.join()
在这个例子中,deadlock_function函数尝试两次获取同一把锁。第一次获取锁成功,但第二次获取锁时,由于锁已经被当前线程持有,导致死锁。程序会卡在第二次获取锁的地方,无法继续执行。
递归锁(Reentrant Lock)
递归锁就像是一个聪明的门锁,它知道你已经在房间里了,所以如果你再试图进入,它会让你进去,而不会把你锁在外面。这在递归函数或需要多次获取同一把锁的情况下特别有用。
让我们看看递归锁是如何工作的:
import threading
rlock = threading.RLock()
def recursive_function(level):
with rlock:
print(f"{threading.current_thread().name} has entered level {level}")
if level > 0:
recursive_function(level - 1)
print(f"{threading.current_thread().name} is leaving level {level}")
thread = threading.Thread(target=recursive_function, args=(3,))
thread.start()
thread.join()
在这个例子中,我们使用了递归锁RLock。recursive_function函数会递归调用自己,并在每个递归层级获取同一把锁。递归锁允许同一个线程多次获取锁,而不会导致死锁。
递归锁 vs 普通锁
- 普通锁:简单高效,但同一个线程不能多次获取同一把锁,否则会导致死锁。
- 递归锁:允许同一个线程多次获取同一把锁,适用于递归调用或需要多次获取锁的情况,但开销稍大。
更清晰的案例
递归锁:
import threading
recursive_lock = threading.RLock()
def recursive_function(n):
if n <= 0:
return
recursive_lock.acquire()
print(f"Acquired lock, n={n}")
recursive_function(n-1)
recursive_lock.release()
print(f"Released lock, n={n}")
recursive_function(3)
输出:
Acquired lock, n=3
Acquired lock, n=2
Acquired lock, n=1
Released lock, n=1
Released lock, n=2
Released lock, n=3
普通锁:
import threading
recursive_lock = threading.Lock()
def recursive_function(n):
if n <= 0:
return
recursive_lock.acquire()
print(f"Acquired lock, n={n}")
recursive_function(n-1)
recursive_lock.release()
print(f"Released lock, n={n}")
recursive_function(3)
运行一天的输出:
Acquired lock, n=3
结论:
锁在多线程编程中是必不可少的工具。普通锁适用于简单的同步场景,而递归锁则在复杂的递归或多次锁定场景中大显身手。