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

时间:2015-12-23 07:29:12

标签: python asynchronous tornado future coroutine

我有一些 Tornado 的协程相关问题。

有一些python-model A ,它具有执行某些功能的能力。可以从模型外部设置该功能。我不能改变模型本身,但我可以传递任何我想要的功能。我试图通过我的功能教它与Tornado的ioloop一起工作,但我不能。

以下是摘录:

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

所以问题是我将函数设置在代码的一部分中,但是使用模型的方法在另一部分中执行它。

现在,Tornado 4.2.1给了我“ IOLoop已经在运行”,但在Tornado 3.1.1中它可行(但我不知道究竟是怎样)。

我知道接下来的事情:

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

    所以,我的问题是:有没有机会使用当前的IOLoop从同步模型的方法执行异步genlist

2 个答案:

答案 0 :(得分:2)

您无法在此处重启外部IOLoop。您有三种选择:

  1. 在任何地方使用异步接口:将a.execute()以及堆栈顶部的所有内容更改为协同程序。这是基于Tornado的应用程序的常用模式;试图跨越同步和异步世界是困难的,最好留在一边或另一边。
  2. 对临时run_sync()使用IOLoop。这就是Tornado的同步tornado.httpclient.HTTPClient所做的事情,这使得从另一个IOLoop内进行调用变​​得安全。但是,如果你这样做,外部IOLoop仍然被阻止,所以你没有通过使genlist异步获得任何东西。
  3. 在一个单独的线程上运行a.execute并回调主内部函数的主IOLoop线程。如果a.execute无法异步,这是避免在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)
    

答案 1 :(得分:0)

说,你的功能看起来像这样:

module test_register;

//inputs
reg [7:0] D;
reg Clk;
reg Clear;
reg Enable;

//outputs
reg [7:0] OutNum;

//instantiate
Register8bit uut(
    .OutNum  (OutNum),  // <---- added missing output
    .D(D),
    .Clk(Clk),
    .Clear(Clear),
    .Enable(Enable)
);

always #50 Clk = ~Clk;

always @(negedge Clk) begin
    $display($time, " clr=%b en=%b D=%b OutNum=%b", Clear, Enable, D, OutNum);
end

initial begin
    D = 'b10001111; // <---- use 'b
    Clk = 1;

    #50;

    //step 1
    #100;
    Clear = 0;
    Enable = 0;
    #100;

    //step 2
    Clear = 0;
    Enable = 1;
    #100;

    //step 3
    Clear = 1;
    Enable = 0;
    #100;

    //step 4
    Clear = 1;
    Enable = 1;
    #100;

    //step 5
    Clear = 0;
    Enable = 1;
    #100;
    #500 $finish;
end
endmodule

/*

Prints out:

                  50 clr=x en=x D=10001111 OutNum=xxxxxxxx
                 150 clr=0 en=0 D=10001111 OutNum=xxxxxxxx
                 250 clr=0 en=1 D=10001111 OutNum=xxxxxxxx
                 350 clr=1 en=0 D=10001111 OutNum=10001111
                 450 clr=1 en=1 D=10001111 OutNum=10001111
                 550 clr=0 en=1 D=10001111 OutNum=00000000
                 650 clr=0 en=1 D=10001111 OutNum=10001111
                 750 clr=0 en=1 D=10001111 OutNum=10001111
                 850 clr=0 en=1 D=10001111 OutNum=10001111
                 950 clr=0 en=1 D=10001111 OutNum=10001111
                1050 clr=0 en=1 D=10001111 OutNum=10001111

*/

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

您可以像这样运行@concurrent.run_on_executor def bar(i=1): # does slow things

foo()

您可以运行from tornado.ioloop import IOLoop loop = IOLoop.current() loop.run_sync(foo) 或任何带有args的协程,如下所示:

bar(..)