我试图转换一些相当简单的gevent代码来使用Tornado的异步设施。下面的示例代码使用ZMQ库来执行非常简单的请求响应。
import zmq.green as zmq
def fun():
i = zmq.Context.instance()
sock = i.socket(zmq.REQ)
sock.connect('tcp://localhost:9005')
sock.send('Ping')
return sock.recv()
我可以在代码中的任何位置以fun()
运行它。 .recv()
调用在等待回复时阻塞,gevent
集线器可以调度代码的其他部分。收到值时,函数返回值。
我读了problems that can arise with these implicit returns,我想使用Tornado IOLoop运行它(也因为我想在IPython Notebook中运行它)。以下是一个选项,其中recv_future()
返回包含结果的Future
:
@gen.coroutine
def fun():
i = zmq.Context.instance()
sock = i.socket(zmq.REQ)
sock.connect('tcp://localhost:9005')
sock.send('Ping')
msg = yield recv_future(sock)
print "Received {}".format(msg[0])
raise gen.Return(msg)
def recv_future(socket):
zmqstream = ZMQStream(socket) # Required for ZMQ
future = Future()
def _finish(reply):
future.set_result(reply)
zmqstream.on_recv(_finish)
return future
问题是现在fun()
不是一个函数,而是一个生成器。因此,如果我需要从另一个函数调用它,我需要使用yield fun()
。但随后调用函数也变成了生成器!
构建使用Python生成器的代码的正确方法是什么?我是否必须使每个功能都成为发电机才能使其工作?如果我需要从__init__()
调用其中一个函数怎么办?那还应该成为一个发电机吗?
答案 0 :(得分:1)
如果我需要从
__init__()
调用其中一个函数怎么办?应该 那也成了发电机?
这是使用yield
/ yield from
进行显式异步编程的当前未解决的问题之一(在Python 3.3+上)。魔术方法不支持它们。您可以从Python核心开发人员那里了解有关此问题here的异步编程的一些有趣想法。
构建使用Python生成器的代码的正确方法是什么? 我是否必须使每个功能都成为发电机才能使其工作? 不是每个函数,而是每个函数都要调用协程,并等待该协程在继续之前完成。切换到显式异步编程模型时,通常需要全押 - 整个程序在龙卷风ioloop内部运行。所以,通过这个玩具示例,您可以这样做:
from tornado.ioloop import IOLoop
from tornado.gen import coroutine
from tornado.concurrent import Future
@gen.coroutine
def fun():
i = zmq.Context.instance()
sock = i.socket(zmq.REQ)
sock.connect('tcp://localhost:9005')
sock.send('Ping')
msg = yield recv_future(sock)
print "Received {}".format(msg[0])
raise gen.Return(msg)
def recv_future(socket):
zmqstream = ZMQStream(socket) # Required for ZMQ
future = Future()
def _finish(reply):
future.set_result(reply)
zmqstream.on_recv(_finish)
return future
if __name__ == "__main__":
ioloop = IOLoop.instance()
ioloop.add_callback(fun)
ioloop.start() # This will run fun, and then block forever.
#ioloop.run_sync(fun) # This will start the ioloop, run fun, then stop the ioloop
看起来您可以通过IPython.kernel API访问ioloop IPython:
In [4]: from IPython.kernel.ioloop import manager
In [5]: manager.ioloop.IOLoop.instance()
Out[5]: <zmq.eventloop.ioloop.ZMQIOLoop at 0x4249ac8>