【问题标题】:Using Pyramid events and multithreading使用 Pyramid 事件和多线程
【发布时间】:2020-08-11 18:07:16
【问题描述】:

我想将事件订阅/通知与多线程一起使用。听起来它应该在理论上起作用,并且文档不包含任何警告。事件应该是同步的,所以也不能延迟。

但实际上,当我通知主线程时,什么都没有进来:

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】:

    get_current_registry() 在处理请求或配置时尝试访问线程局部变量 Pyramid 寄存器,以告诉线程哪些 Pyramid 应用程序当前在该线程中处于活动状态。这里的问题是 get_current_registry() 总是返回一个注册表,而不是你想要的,所以很难看出它为什么不起作用。

    生成新线程时,您需要将 Pyramid 应用程序注册为当前线程本地。最好的方法是使用pyramid.scripting.prepare。 “简单”的方法就是在你的线程中再次运行引导程序。不过,我会展示“正确”的方式。

    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()创建语句并且不传播到thread()方法。即在“get_current_registry”调用具有“with”环境上下文的情况下,但这个“with”上下文不会传播到线程运行“get_current_registry”的位置。因此,您需要将 env 传播到 thread() - 也许通过创建一个简单的可运行类,该类在 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()
      

      【讨论】:

      • 由于某种原因,这对我来说真的不起作用。即使在self.env 下,也不会调用事件订阅者。
      • 这里的问题是您不能多次重复使用env 作为上下文管理器。我在回答中提供了适当的方式。
      猜你喜欢
      • 2017-07-19
      • 2013-04-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多