Python 如何在同步环境中执行Tornado协程?

Python 如何在同步环境中执行Tornado协程?,python,asynchronous,tornado,future,coroutine,Python,Asynchronous,Tornado,Future,Coroutine,我有一些与Tornado的协同程序相关的问题 有一些python模型A,它具有执行某些函数的能力。可以从模型外部设置该功能。我不能改变模型本身,但我可以传递任何我想要的函数。我试图通过我的函数来教它如何使用Tornado的iLoop,但我做不到 以下是片段: import functools import pprint from tornado import gen from tornado import ioloop class A: f = None def execute

我有一些与Tornado的协同程序相关的问题

有一些python模型A,它具有执行某些函数的能力。可以从模型外部设置该功能。我不能改变模型本身,但我可以传递任何我想要的函数。我试图通过我的函数来教它如何使用Tornado的iLoop,但我做不到

以下是片段:

import functools
import pprint
from tornado import gen
from tornado import ioloop

class A:
    f = None
    def execute(self):
      return self.f()
    pass

@gen.coroutine
def genlist():
    raise gen.Return(range(1, 10))

@gen.coroutine
def some_work():
    a = A()
    a.f = functools.partial(
        ioloop.IOLoop.instance().run_sync,
        lambda: genlist())
    print "a.f set"
    raise gen.Return(a)

@gen.coroutine
def main():
    a = yield some_work()
    retval = a.execute()
    raise gen.Return(retval)


if __name__ == "__main__":
    pprint.pprint(ioloop.IOLoop.current().run_sync(main))
所以问题是,我在代码的一部分设置函数,但在另一部分使用模型的方法执行它

现在,Tornado4.2.1给了我“iLoop已经在运行”的信息,但在Tornado3.1.1中它可以工作(但我不知道具体是如何工作的)

我知道接下来的事情:

  • 我可以创建新的ioloop,但我想使用现有的ioloop
  • 我可以用一些函数包装genlist,这些函数知道genlist的结果是Future,但我不知道如何阻止执行,直到Future的结果被设置在同步函数中
  • 另外,我不能将.execute()的结果用作将来的对象,因为可以从代码的其他部分调用.execute(),即它应该返回list实例


    所以,我的问题是:是否有机会使用当前的IOLoop从同步模型的方法执行异步的
    genlist
    。您有三种选择:

  • 在任何地方都使用异步接口:将
    a.execute()
    和堆栈顶部的所有内容更改为协程。这是基于Tornado的应用程序的常见模式;试图跨越同步和异步世界是困难的,最好是站在这一边或那一边
  • 在临时
    IOLoop
    上使用
    run\u sync()
    。这就是Tornado的synchronous
    Tornado.httpclient.httpclient
    所做的,这使得从另一个
    IOLoop
    中调用变得安全。但是,如果这样做,外部IOLoop仍然被阻塞,因此通过使
    genlist
    异步,您将一无所获
  • 在一个单独的线程上运行
    a.execute
    ,并为内部函数调用主IOLoop的线程。如果
    a.execute
    不能设置为异步,这是避免在IOLoop运行时阻塞IOLoop的唯一方法

    executor = concurrent.futures.ThreadPoolExecutor(8)
    
    @gen.coroutine
    def some_work():
        a = A()
        def adapter():
            # Convert the thread-unsafe tornado.concurrent.Future
            # to a thread-safe concurrent.futures.Future.
            # Note that everything including chain_future must happen
            # on the IOLoop thread.
            future = concurrent.futures.Future()
            ioloop.IOLoop.instance().add_callback(
                lambda: tornado.concurrent.chain_future(
                    genlist(), future)
            return future.result()
        a.f = adapter
        print "a.f set"
        raise gen.Return(a)
    
    @gen.coroutine
    def main():
        a = yield some_work()
        retval = yield executor.submit(a.execute)
        raise gen.Return(retval)
    

  • 比如,您的函数如下所示:

    @gen.coroutine
    def foo():
        # does slow things
    

    您可以像这样运行
    foo()

    from tornado.ioloop import IOLoop
    loop = IOLoop.current()
    
    loop.run_sync(foo)
    
    您可以运行
    bar(…)
    ,或任何采用args的协同程序,如下所示:

    from functools import partial
    from tornado.ioloop import IOLoop
    
    loop = IOLoop.current()
    
    f = partial(bar, i=100)
    loop.run_sync(f)
    
    from functools import partial
    from tornado.ioloop import IOLoop
    
    loop = IOLoop.current()
    
    f = partial(bar, i=100)
    loop.run_sync(f)