当多个线程仅有一个线程能够写入特定数据时,使用读写锁可以提高程序的性能。Python提供threading
模块支持读写锁实现,而读写锁的实现基于RLock
对象。读写锁的实现能够控制多个线程同时读取一个文件或者同一时刻只允许一个线程写入一个文件。
使用threading
模块的RLock()
方法创建一个新的读写锁。读写锁可以用来控制对文件或者数据结构的同时访问。
import threading
rwLock = threading.RLock()
要访问被读写锁保护的资源时,必须先获得锁。一个线程最多只能获得一次写锁,但可以多次获得读锁。如果一个线程获得了写锁,则其他线程无法获得读写锁;如果一个线程获得了读锁,则其他线程可以读取但不能写入。
rwLock.acquire_read()
rwLock.acquire_write()
例如:
import threading
rwLock = threading.RLock()
def read_file():
# 获取读锁
rwLock.acquire_read()
try:
with open("file.txt", "r") as f:
data = f.read()
print(data)
finally:
# 释放读锁
rwLock.release()
def write_file():
# 获取写锁
rwLock.acquire_write()
try:
with open("file.txt", "w") as f:
f.write("Hello, world!")
finally:
# 释放写锁
rwLock.release()
上述示例中,read_file()
获取了读锁,而write_file()
获取了写锁。多个线程可以同时调用read_file()
方法来读取文件,但是只有一个线程调用write_file()
方法来写入文件,其他线程必须等待它释放写锁才能访问同一资源。
在下一个示例中,我们将演示如何使用读写锁来保护一个共享的数据结构accounts
。
import threading
class BankAccount:
def __init__(self, balance=0):
self.balance = balance
self.rwLock = threading.RLock()
def deposit(self, amount):
self.rwLock.acquire_write()
try:
self.balance += amount
finally:
self.rwLock.release()
def withdraw(self, amount):
self.rwLock.acquire_write()
try:
if amount <= self.balance:
self.balance -= amount
else:
raise ValueError("Insufficient balance")
finally:
self.rwLock.release()
def get_balance(self):
self.rwLock.acquire_read()
try:
return self.balance
finally:
self.rwLock.release()
def process_transactions(account, transactions):
for transaction, amount in transactions:
if transaction == "deposit":
account.deposit(amount)
elif transaction == "withdraw":
account.withdraw(amount)
else:
raise ValueError("Invalid transaction")
account = BankAccount(1000)
transactions1 = [("deposit", 200), ("withdraw", 100), ("deposit", 50), ("withdraw", 500)]
transactions2 = [("deposit", 500), ("withdraw", 100), ("deposit", 1000)]
t1 = threading.Thread(target=process_transactions, args=(account, transactions1))
t2 = threading.Thread(target=process_transactions, args=(account, transactions2))
t1.start()
t2.start()
t1.join()
t2.join()
print(f"Final balance: {account.get_balance()}")
在上述示例中,BankAccount
对象有一个balance
成员变量,它被读写锁保护。因此多个线程可以同时访问这个对象的deposit()
,withdraw()
和get_balance()
方法,但是在修改balance
时只能有一个线程写操作,其他线程都必须等待写操作完成后才能再次读取或者写入。
值得注意的是,除非在代码中特别需要,否则不要做过多的锁定/解锁。频繁的锁定/解锁可能会导致代码变慢。在上述示例中,得到锁时一定要及时释放它们,否则会导致死锁的情况。