面试题答案
一键面试可能出现的线程安全问题示例
假设有一个简单的图书排名数据结构,用字典表示,键为图书ID,值为排名分数。例如:book_rank = {'book1': 10, 'book2': 5}
。
当多个线程同时对图书排名进行操作,比如增加某本图书的分数时,可能出现以下问题:
- 数据竞争:
import threading
book_rank = {'book1': 10}
def increase_rank():
rank = book_rank['book1']
rank = rank + 1
book_rank['book1'] = rank
threads = []
for _ in range(10):
t = threading.Thread(target=increase_rank)
threads.append(t)
t.start()
for t in threads:
t.join()
print(book_rank['book1'])
在这个例子中,多个线程同时读取book1
的排名,然后各自增加1,再写回。由于线程执行顺序不确定,可能会导致最终结果小于预期的20(假设预期每个线程都成功增加1)。这是因为多个线程在读取和写入数据之间存在时间差,导致数据被覆盖,出现数据不一致。
使用锁机制解决问题
Python中的threading.Lock
可以用来解决上述问题。
import threading
book_rank = {'book1': 10}
lock = threading.Lock()
def increase_rank():
global book_rank
lock.acquire()
try:
rank = book_rank['book1']
rank = rank + 1
book_rank['book1'] = rank
finally:
lock.release()
threads = []
for _ in range(10):
t = threading.Thread(target=increase_rank)
threads.append(t)
t.start()
for t in threads:
t.join()
print(book_rank['book1'])
在上述代码中,lock.acquire()
获取锁,确保只有一个线程可以进入临界区(读取、修改和写入图书排名的代码段)。try - finally
语句确保无论在临界区内发生什么,锁都会被释放(通过lock.release()
),这样其他线程才有机会获取锁并操作数据,从而避免了数据竞争问题,保证了线程安全。