上下文的实现一般可以通过装饰器或者上下文管理器实现,装饰器确保函数可以运行在正确的上下文中,或者在函数前后运行一些代码。
当一个数据项需要在多个线程之间共享时,就要用一个锁来保护它避免多次访问。这个锁可以在装饰器中编写(当然也可以不使用修饰器),代码如下:
from threading import RLock
lock = RLock()
def synchronized(function):
def _synchronized(*args, **kw):
lock.acquire()
try:
return function(*args, **kw)
finally:
lock.release()
return _synchronized
@synchronized
def thread_safe(): # 确保锁定资源
pass
关于修饰器的构建本系列的文章已经介绍过了,不再赘述,在代码中创建了一个互斥锁,这里是将函数调用作为临界区,防止被多次调用。关于互斥锁的使用暂时不必深究,只需要知道他是一种线程共享的方式即可。
为了确保即使在出现错误的情况下也能运行某些清理代码,try…finally语句是很有用的。如关闭文件、释放锁、创建临时代码补丁等功能。
with语句为这些使用场景下的代码块包装提供了一种简单方法。即使该代码块引发了异常,也可以在其执行前后调用一些代码。
我们熟悉的文件打开方式就是如此,假设我们有一个名为:test.txt的文件,打开过程可以是以下方式打开:
with open("test.txt") as f:
# 可以在这里进行对文件的操作
pass
上面打开方式的优点就是不需要调用close对文件进行关闭,保证了文件随用随关。
任何实现了上下文管理器协议(context manager protocol)的对象都可以用作上下文管理器。该协议包含两个特殊方法。
- __ enter __ (self):
- __ exit __ (self, exc _ type, exc _ value, traceback):
简而言之,执行with语句的过程如下:
__ exit __
接受代码块中出现错误时填入的3个参数。如果没有出现错误,那么这3个参数都被设为None。
出现错误时,__ exit__不会重新引发这个错误,但它可以通过返回True来避免引发异常。
下面是某个实现了这一协议的上下文管理器示例
class ContextIllustration:
def __enter__(self):
print('entering context')
def __exit__(self, exc_type, exc_value, traceback):
print('leaving context')
if exc_type is None:
print('with no error')
else:
print('with an error (%s)' % exc_value)
在这里enter就是来凑数的,我们主要来看exit的部分
当遇到报错时三个参数不为None,所以只需要判断其中一个参数就可以得知是否有报错了。其中exc_type是异常值类型, exc_value是异常值。
正常调用未引起报错的情况如下:
with ContextIllustration():
print("inside")
运行结果:
entering context
inside
leaving context
with no error
为了营造错误使用的情况,我们这里直接抛出错误
entering context
leaving context
with an error (raised within 'with')