面试题答案
一键面试使用with
语句确保线程优雅退出并释放资源
- 原理:
with
语句会在进入代码块时调用对象的__enter__
方法,在离开代码块时(无论是正常结束还是因为异常)调用对象的__exit__
方法。这使得资源管理更加自动化和安全。 - 示例:
import threading
import time
class Resource:
def __enter__(self):
print("Entering resource management")
# 模拟获取资源,如打开文件、建立数据库连接等
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("Exiting resource management")
# 模拟释放资源,如关闭文件、断开数据库连接等
if exc_type:
print(f"An exception occurred: {exc_type}, {exc_val}, {exc_tb}")
return True
def worker():
with Resource() as res:
print("Inside the with block")
time.sleep(2)
raise ValueError("Simulated error")
t = threading.Thread(target=worker)
t.start()
t.join()
在上述代码中,Resource
类实现了上下文管理器协议。worker
函数使用with
语句来管理Resource
实例。无论worker
函数内部是否发生异常,Resource
的__exit__
方法都会被调用,从而确保资源被正确释放。
contextlib.ExitStack
在该场景下的应用原理及优势
- 应用原理:
ExitStack
提供了一种方便的方式来管理多个上下文管理器。它允许你动态地添加上下文管理器,并在ExitStack
对象退出时按照后进先出(LIFO)的顺序调用这些上下文管理器的__exit__
方法。 - 优势:
- 动态管理:适用于需要在运行时动态决定需要管理哪些资源的场景。例如,根据不同的条件打开不同的文件或建立不同的数据库连接。
- 简化代码:避免了多个嵌套的
with
语句,使代码更加简洁。 - 统一管理:可以将多个资源的释放操作统一在一个
ExitStack
中管理,提高了代码的可读性和可维护性。
- 示例:
import threading
import contextlib
import time
def worker():
with contextlib.ExitStack() as stack:
# 动态添加资源管理
res1 = stack.enter_context(Resource())
res2 = stack.enter_context(Resource())
print("Inside the ExitStack block")
time.sleep(2)
raise ValueError("Simulated error")
t = threading.Thread(target=worker)
t.start()
t.join()
在这个例子中,ExitStack
用于管理两个Resource
实例。当ExitStack
退出时,会依次调用两个Resource
实例的__exit__
方法,确保资源被正确释放,同时代码结构更加清晰,无需嵌套的with
语句。