如何使 2.7 python 上下文管理器线程安全

2024-01-02

我有一个在 Django 服务上运行的大型 python 应用程序。我需要关闭某些操作的权限测试,因此我创建了这个上下文管理器:

class OverrideTests(object):

    def __init__(self):
        self.override = 0

    def __enter__(self):
        self.override += 1

    # noinspection PyUnusedLocal
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.override -= 1
        assert not self.override < 0

    @property
    def overriding(self):
        return self.override > 0

override_tests = OverrideTests()

然后,应用程序的各个部分可以使用上下文管理器覆盖测试:

with override_tests:
    do stuff
    ...

在 do stuff 中,上述上下文管理器可以在不同的函数中多次使用。计数器的使用可以控制这种情况,并且看起来工作正常......直到线程介入。

一旦涉及到线程,全局上下文管理器就会被重新使用,因此,测试可能会被错误地覆盖。

这是一个简单的测试用例 - 如果thread.start_new_thread(do_id, ())线被替换为简单的do_it但严重失败,如下所示:

def stat(k, expected):
    x = '.' if override_tests.overriding == expected else '*'
    sys.stdout.write('{0}{1}'.format(k, x))


def do_it_inner():
    with override_tests:
        stat(2, True)
    stat(3, True)  # outer with context makes this true


def do_it():
    with override_tests:
        stat(1, True)
        do_it_inner()
    stat(4, False)


def do_it_lots(ntimes=10):
    for i in range(ntimes):
        thread.start_new_thread(do_it, ())

如何使这个上下文管理器线程安全,以便在每个 Python 线程中,即使它是可重入的,也能一致地使用它?


这是一种方法seems工作:使您的 OverrideTests 类成为以下类的子类threading.local。为了安全起见,你应该调用超类__init__在你的__init__(尽管即使你不这样做它似乎也能工作):

class OverrideTests(threading.local):

    def __init__(self):
        super(OverrideTests, self).__init__()
        self.override = 0

    # rest of class same as before

override_tests = OverrideTests()

Then:

>>> do_it_lots()
1.1.1.2.2.1.1.1.1.1.1.3.3.2.2.2.2.2.2.4.4.3.1.3.3.3.3.4.3.2.4.4.2.4.3.4.4.4.3.4.

但是,我不会把钱花在这种不会在某种极端情况下失败的情况上,特别是如果您的实际应用程序比您在此处显示的示例更复杂的话。最终,您确实应该重新考虑您的设计。在您的问题中,您关注的是如何“使上下文管理器线程安全”。但真正的问题不仅在于你的上下文管理器,还在于你的function (stat在你的例子中)。stat依赖于全局状态(全局override_tests),这在线程环境中本质上是脆弱的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使 2.7 python 上下文管理器线程安全 的相关文章

随机推荐