面试题答案
一键面试利用上下文管理器和锁避免资源竞争
在Python中,可以通过在上下文管理器类中使用锁来避免资源竞争。以下是一个简单的示例,以文件操作为例:
import threading
import time
class FileContextManager:
def __init__(self, file_path, mode):
self.file_path = file_path
self.mode = mode
self.lock = threading.Lock()
self.file = None
def __enter__(self):
self.lock.acquire()
self.file = open(self.file_path, self.mode)
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
if self.file:
self.file.close()
self.lock.release()
def worker():
with FileContextManager('test.txt', 'a') as file:
file.write(f'Thread {threading.current_thread().name} is writing\n')
time.sleep(1)
threads = []
for _ in range(5):
thread = threading.Thread(target=worker)
threads.append(thread)
thread.start()
for thread in threads:
thread.join()
在上述代码中:
FileContextManager
类是一个上下文管理器,它在初始化时创建了一个锁self.lock
。- 在
__enter__
方法中,先获取锁,然后打开文件并返回文件对象。 - 在
__exit__
方法中,关闭文件并释放锁。
这样,在多线程环境下,当一个线程进入上下文管理器时,它会获取锁,确保其他线程无法同时访问共享资源(文件),从而避免资源竞争。
性能优化策略及代码示例
1. 使用信号量控制并发访问数量
信号量可以控制同时访问共享资源的线程数量,从而在一定程度上提高性能。例如,假设我们希望同时最多有2个线程访问文件:
import threading
import time
class FileContextManagerWithSemaphore:
def __init__(self, file_path, mode):
self.file_path = file_path
self.mode = mode
self.semaphore = threading.Semaphore(2)
self.file = None
def __enter__(self):
self.semaphore.acquire()
self.file = open(self.file_path, self.mode)
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
if self.file:
self.file.close()
self.semaphore.release()
def worker_with_semaphore():
with FileContextManagerWithSemaphore('test.txt', 'a') as file:
file.write(f'Thread {threading.current_thread().name} is writing\n')
time.sleep(1)
threads = []
for _ in range(5):
thread = threading.Thread(target=worker_with_semaphore)
threads.append(thread)
thread.start()
for thread in threads:
thread.join()
2. 采用读写锁(适用于读多写少场景)
如果共享资源的读操作远多于写操作,可以使用读写锁(RLock
)来提高性能。读操作可以并发执行,而写操作则需要独占资源。
import threading
import time
class FileContextManagerWithRLock:
def __init__(self, file_path, mode):
self.file_path = file_path
self.mode = mode
self.read_lock = threading.Lock()
self.write_lock = threading.Lock()
self.read_count = 0
self.file = None
def acquire_read_lock(self):
with self.read_lock:
self.read_count += 1
if self.read_count == 1:
self.write_lock.acquire()
def release_read_lock(self):
with self.read_lock:
self.read_count -= 1
if self.read_count == 0:
self.write_lock.release()
def __enter__(self):
if 'r' in self.mode:
self.acquire_read_lock()
else:
self.write_lock.acquire()
self.file = open(self.file_path, self.mode)
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
if self.file:
self.file.close()
if 'r' in self.mode:
self.release_read_lock()
else:
self.write_lock.release()
def read_worker():
with FileContextManagerWithRLock('test.txt', 'r') as file:
content = file.read()
print(f'Thread {threading.current_thread().name} read: {content}')
def write_worker():
with FileContextManagerWithRLock('test.txt', 'a') as file:
file.write(f'Thread {threading.current_thread().name} is writing\n')
time.sleep(1)
read_threads = []
write_threads = []
for _ in range(3):
read_thread = threading.Thread(target=read_worker)
read_threads.append(read_thread)
read_thread.start()
for _ in range(2):
write_thread = threading.Thread(target=write_worker)
write_threads.append(write_thread)
write_thread.start()
for thread in read_threads:
thread.join()
for thread in write_threads:
thread.join()
在上述代码中,读操作通过acquire_read_lock
和release_read_lock
方法来管理读锁,允许多个读操作并发进行;而写操作则直接获取写锁,确保独占资源。这种方式在读写比例不同的场景下可以显著提高性能。