简体   繁体   English

使用 Pyramid 事件和多线程

[英]Using Pyramid events and multithreading

I'd like to use events subscription / notification together with multithreading.我想将事件订阅/通知与多线程一起使用。 It sounds like it should just work in theory and the documentation doesn't include any warnings.听起来它应该在理论上起作用,并且文档不包含任何警告。 The events should be synchronous, so no deferring either.事件应该是同步的,所以也不能延迟。

But in practice, when I notify off the main thread, nothing comes in:但在实践中,当我通知主线程时,什么也没有出现:

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())

Is this not expected to work?预计这不会起作用吗? Or am I doing something wrong?还是我做错了什么?

I tried also the suggested solution.我也尝试了建议的解决方案。 It doesn't print the expected event.它不会打印预期的事件。

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()

get_current_registry() is trying to access the threadlocal variable Pyramid registers when processing requests or config to tell the thread what Pyramid app is currently active IN THAT THREAD. get_current_registry()在处理请求或配置时尝试访问线程局部变量 Pyramid 寄存器,以告诉线程 Pyramid 应用程序当前在该线程中处于活动状态。 The gotcha here is that get_current_registry() always returns a registry, just not the one you want, so it's hard to see why it's not working.这里的问题是get_current_registry()总是返回一个注册表,而不是您想要的注册表,所以很难看出它为什么不起作用。

When spawning a new thread, you need to register your Pyramid app as the current threadlocal.生成新线程时,您需要将 Pyramid 应用程序注册为当前线程本地。 The best way to do this is with pyramid.scripting.prepare .最好的方法是使用pyramid.scripting.prepare The "easy" way is just to run bootstrap again in your thread. “简单”的方法就是在你的线程中再次运行引导程序。 I'll show the "right" way though.不过,我会展示“正确”的方式。

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 is what bootstrap uses under the hood, and is a lot more efficient than running bootstrap multiple times because it shares the registry and all of your app configuration instead of making a completely new copy of your app. pyramid.scripting.prepare是 bootstrap 在后台使用的,并且比多次运行 bootstrap 更有效,因为它共享注册表和所有应用程序配置,而不是制作应用程序的全新副本。

Is it just that the 'with' context applies to the Thread() create statement only and does not propogate to the thread() method.仅仅是“with”上下文适用于 Thread() create 语句而不传播到 thread() 方法。 ie in the case that works the 'get_current_registry' call has 'with' env context, but this 'with' context will not propogate to the point where the thread runs the 'get_current_registry'.即在“get_current_registry”调用具有“with”环境上下文的情况下,但这个“with”上下文不会传播到线程运行“get_current_registry”的位置。 So you need to propogate the env to the thread() - perhaps by creating a simple runnable class that takes the env in the init method.因此,您需要将 env 传播到 thread() - 可能通过创建一个简单的可运行 class 来获取 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()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM