面试题答案
一键面试资源竞争场景
在Python多线程编程中,当多个线程同时访问和修改共享资源(如全局变量、文件、数据库连接等)时,就可能出现资源竞争。例如:
import threading
counter = 0
def increment():
global counter
for _ in range(1000000):
counter = counter + 1
threads = []
for _ in range(2):
t = threading.Thread(target=increment)
threads.append(t)
t.start()
for t in threads:
t.join()
print(counter)
上述代码中,两个线程同时对counter
进行累加操作,由于CPU调度的不确定性,可能导致最终结果与预期不符,这就是资源竞争。
死锁场景
死锁通常发生在多个线程相互等待对方释放资源的情况下。例如:
import threading
lock1 = threading.Lock()
lock2 = threading.Lock()
def thread1():
lock1.acquire()
print("Thread 1 acquired lock1")
lock2.acquire()
print("Thread 1 acquired lock2")
lock2.release()
lock1.release()
def thread2():
lock2.acquire()
print("Thread 2 acquired lock2")
lock1.acquire()
print("Thread 2 acquired lock1")
lock1.release()
lock2.release()
t1 = threading.Thread(target=thread1)
t2 = threading.Thread(target=thread2)
t1.start()
t2.start()
t1.join()
t2.join()
在上述代码中,thread1
先获取lock1
,thread2
先获取lock2
,然后它们都试图获取对方已持有的锁,从而导致死锁。
预防死锁和处理资源竞争的设计方案
- 使用锁(Lock):通过对共享资源的访问加锁,同一时间只有一个线程可以访问共享资源。
import threading
counter = 0
lock = threading.Lock()
def increment():
global counter
for _ in range(1000000):
lock.acquire()
counter = counter + 1
lock.release()
threads = []
for _ in range(2):
t = threading.Thread(target=increment)
threads.append(t)
t.start()
for t in threads:
t.join()
print(counter)
- 使用信号量(Semaphore):信号量可以控制同时访问共享资源的线程数量。
import threading
semaphore = threading.Semaphore(2) # 允许两个线程同时访问
def access_shared_resource():
semaphore.acquire()
try:
# 访问共享资源的代码
print(threading.current_thread().name, "accessed shared resource")
finally:
semaphore.release()
threads = []
for i in range(5):
t = threading.Thread(target=access_shared_resource)
threads.append(t)
t.start()
for t in threads:
t.join()
- 使用条件变量(Condition):条件变量用于线程间的复杂同步,当某个条件满足时,通知其他线程。
import threading
condition = threading.Condition()
resource = None
def producer():
global resource
with condition:
resource = "Some data"
condition.notify() # 通知消费者数据已准备好
def consumer():
with condition:
condition.wait() # 等待生产者通知
print("Consumer got:", resource)
t1 = threading.Thread(target=producer)
t2 = threading.Thread(target=consumer)
t2.start()
t1.start()
t1.join()
t2.join()
优缺点分析
- 锁(Lock)
- 优点:简单直接,能有效防止资源竞争。
- 缺点:如果使用不当,容易导致死锁;并且会降低程序并发度,因为同一时间只有一个线程能访问共享资源。
- 信号量(Semaphore)
- 优点:可以控制同时访问资源的线程数量,适用于对资源访问数量有限制的场景,能在一定程度上提高并发度。
- 缺点:同样存在死锁风险,如果信号量的获取和释放顺序不当。
- 条件变量(Condition)
- 优点:适用于线程间需要复杂同步的场景,能实现更灵活的线程间通信和同步。
- 缺点:使用相对复杂,需要仔细设计条件判断和通知逻辑,否则容易出现逻辑错误。