使用金字塔事件和多线程

问题描述 投票:0回答:1

我想把事件订阅通知和多线程一起使用。这听起来好像只是理论上的工作,文档中也没有任何警告。事件应该是同步的,所以也不会延迟。

但实际上,当我在主线程外通知时,没有任何东西进来。

def run():
    logging.config.fileConfig(sys.argv[1])
    with bootstrap(sys.argv[1]) as env:
        get_current_registry().notify(FooEvent())  # <- works
        Thread(target=thread).start()              # <- doesn't work

def thread():
    get_current_registry().notify(FooEvent())

这样做不应该吗?还是我做错了什么?

我也尝试了建议的解决方案。它没有打印出预期的事件。

class Foo:
    pass

@subscriber(Foo)
def metric_report(event):
    print(event)

def run():
    with bootstrap(sys.argv[1]) as env:

        def foo(env):
            try:
                with env:
                    get_current_registry().notify(Foo())
            except Exception as e:
                print(e)

        t = Thread(target=foo, args=(env,))
        t.start()
        t.join()
python multithreading pyramid
1个回答
3
投票

get_current_registry() 是试图在处理请求或配置时访问Pyramid注册的线程本地变量,以告诉线程当前在该线程中活动的Pyramid应用是什么。这里的问题是 get_current_registry() 总是返回一个注册表,只是不是你想要的那个,所以很难理解为什么它不能工作。

当生成一个新线程时,你需要将你的Pyramid应用注册为当前线程local。最好的方法是使用 pyramid.scripting.prepare. 简单的方法就是在你的线程中重新运行bootstrap。但我会告诉你 "正确 "的方法。

def run():
    pyramid.paster.setup_logging(sys.argv[1])
    get_current_registry().notify(FooEvent())  # doesn't work, just like in the thread
    with pyramid.paster.bootstrap(sys.argv[1]) as env:
        registry = env['registry']
        registry.notify(FooEvent())  # works
        get_current_registry().notify(FooEvent())  # works
        Thread(target=thread_main, args=(env['registry'],)).start()

def thread_main(registry):
    registry.notify(FooEvent())  # works, but threadlocals are not setup if other code triggered by this invokes get_current_request() or get_current_registry()

    # so let's setup threadlocals
    with pyramid.scripting.prepare(registry=registry) as env:
        registry.notify(FooEvent())  # works
        get_current_registry().notify(FooEvent())  # works

pyramid.scripting.prepare 是bootstrap在引擎盖下使用的东西,比多次运行bootstrap更有效率,因为它共享注册表和所有的应用配置,而不是制作一个全新的应用副本。


2
投票

是否只是'with'上下文适用于Thread()创建语句?只是 也就是说,在这个例子中,'get_current_registry'调用有'with'env上下文,但是这个'with'上下文不会传播到线程运行'get_current_registry'的地方。所以你需要将env传播到线程()--也许通过创建一个简单的可运行类,在init方法中接受env。

class X:
    def __init__(self,env):
        self.env = env

    def __call__(self):
        with self.env:
            get_current_registry().notify(FooEvent())
        return

def run():
    logging.config.fileConfig(sys.argv[1])
    with bootstrap(sys.argv[1]) as env:
        get_current_registry().notify(FooEvent())
        Thread(target=X(env)).start()
© www.soinside.com 2019 - 2024. All rights reserved.