假设:
class A(object):
def __init__(self):
self.cnt = 0
def __enter__(self):
self.cnt += 1
def __exit__(self, exc_type, exc_value, traceback)
self.cnt -= 1
self.cnt += 1
执行两次吗?__enter__
被调用两次而__exit__
只被调用一次,因此self.cnt
最终结果为{{1} }}?答案 0 :(得分:2)
不,只能通过锁来保证线程安全。
多线程可能会
self.cnt += 1
执行两次吗?
如果你有两个运行它的线程,它将被执行两次。三个线程,三次等等。我不确定你的意思是什么,或许告诉我们你是如何构建/执行这些与你的上下文管理器有关的线程的。
对于相同的上下文管理器实例,是否可能在多线程中,以某种方式
__enter__
被调用两次而__exit__
只被调用一次,因此self.cnt的最终结果是1?
是的,最终结果可能不为零,但不是通过您假设的非对称调用进入和退出的机制。如果跨多个线程使用相同的上下文管理器实例,则可以构造一个可以重现错误的简单示例:
from threading import Thread
class Context(object):
def __init__(self):
self.cnt = 0
def __enter__(self):
self.cnt += 1
def __exit__(self, exc_type, exc_value, traceback):
self.cnt -= 1
shared_context = Context()
def run(thread_id):
with shared_context:
print('enter: shared_context.cnt = %d, thread_id = %d' % (
shared_context.cnt, thread_id))
print('exit: shared_context.cnt = %d, thread_id = %d' % (
shared_context.cnt, thread_id))
threads = [Thread(target=run, args=(i,)) for i in range(1000)]
# Start all threads
for t in threads:
t.start()
# Wait for all threads to finish before printing the final cnt
for t in threads:
t.join()
print(shared_context.cnt)
您将不可避免地发现最终的shared_context.cnt
通常不会以0
结束,即使所有线程都已启动并完成相同的代码,即使进入和退出已经所有人都被或多或少地成对称呼:
enter: shared_context.cnt = 3, thread_id = 998
exit: shared_context.cnt = 3, thread_id = 998
enter: shared_context.cnt = 3, thread_id = 999
exit: shared_context.cnt = 3, thread_id = 999
2
...
enter: shared_context.cnt = 0, thread_id = 998
exit: shared_context.cnt = 0, thread_id = 998
enter: shared_context.cnt = 1, thread_id = 999
exit: shared_context.cnt = 0, thread_id = 999
-1
这主要是由于+=
运算符被解析为四个操作码而且只有GIL才能确保单个操作码是安全的。有关详细信息,请参阅此问题:Is the += operator thread-safe in Python?