面试题答案
一键面试信号量(Semaphore)与锁(Lock)的主要差异
- 控制访问数量:
- 锁(Lock):本质上是二元信号量,只允许一个线程进入临界区,用于保证同一时间只有一个线程能访问共享资源,就像一把钥匙对应一扇门,只有拿到钥匙(获取锁)的线程才能进入房间(访问资源)。
- 信号量(Semaphore):可以允许一定数量的线程同时进入临界区,通过设置内部计数器来控制同时访问资源的线程数量,例如可以想象为有多个房间,信号量的计数器表示房间数量,有相应数量的钥匙,多个线程可以同时拿到钥匙进入房间。
- 适用场景:
- 锁(Lock):适用于资源只能被一个线程独占访问的场景,如对共享文件的写入操作,避免数据冲突。
- 信号量(Semaphore):适用于资源允许一定数量线程同时访问的场景,例如数据库连接池,允许一定数量的线程同时获取连接进行数据库操作。
代码示例
- 锁(Lock)示例
import threading
# 共享资源
counter = 0
lock = threading.Lock()
def increment():
global counter
# 获取锁
lock.acquire()
try:
counter = counter + 1
finally:
# 释放锁
lock.release()
threads = []
for _ in range(10):
t = threading.Thread(target=increment)
threads.append(t)
t.start()
for t in threads:
t.join()
print(f"Final counter value: {counter}")
在这个示例中,lock.acquire()
获取锁,确保只有一个线程能修改 counter
,lock.release()
释放锁,让其他线程有机会获取锁并修改 counter
。
- 信号量(Semaphore)示例
import threading
# 创建一个信号量,允许3个线程同时访问
semaphore = threading.Semaphore(3)
def access_resource():
# 获取信号量
semaphore.acquire()
try:
print(f"{threading.current_thread().name} has access to the resource.")
finally:
# 释放信号量
semaphore.release()
threads = []
for i in range(10):
t = threading.Thread(target=access_resource)
threads.append(t)
t.start()
for t in threads:
t.join()
在这个示例中,semaphore.acquire()
获取信号量,当信号量内部计数器大于0时,线程获取成功并将计数器减1,当计数器为0时,其他线程需要等待。semaphore.release()
释放信号量,将计数器加1,允许其他等待的线程获取信号量并访问资源。