Python-tornado 中一段关于 gen 模块的使用, 其逻辑如何理解?

问题

如同自己当初理解回调一样, ( 一旦回调层次多了, 就感觉理解起来有点难度 )

详细问题已经在代码中注明, 见相关代码

相关代码

from tornado import gen
from tornado.ioloop import IOLoop
from tornado.queues import Queue

q = Queue(maxsize=2)

@gen.coroutine
def consumer():
    while True:
        item = yield q.get()
        try:
            print('Doing work on %s' % item)
            yield gen.sleep(0.01)
        finally:
            q.task_done()

@gen.coroutine
def producer():
    for item in range(5):
        yield q.put(item)
        print('Put %s' % item)

@gen.coroutine
def main():
    # Start consumer without waiting (since it never finishes).
    IOLoop.current().spawn_callback(consumer)
    yield producer()     # Wait for producer to put all tasks.
    yield q.join()       # Wait for consumer to finish all tasks.
    print('Done')

IOLoop.current().run_sync(main)

如上代码, 初看, 能懂它的大概意思, 但是具体一行一行看下来, 它的执行顺序就不是很清楚了, 下面是我的一些理解
1. 进入 main
2. consumer 派生一个回调, 在那里等待
3. producer 协程, 不断进入又出来( 我所理解的协程: 不断进入, 不断出来 )
4. q.join 协程, 卡住

以上理解, 有何错误的地方?
阅读 3.3k
2 个回答

yield 是让出时间片的意思。yield producer() 就是把时间交给 producer(),所以它自己是处于等待状态。

这里并没有(显式的)回调。

协程即「协作式多线程」,一个线程主动让出时间片给另外的线程。yield 一次,当然只让出一次。

main 这个协程会先在 yield producer() 处等待。五个任务生产完毕之后,它才会接着到 yield q.join() 这里等待任务处理完毕。

from tornado import gen
from tornado.ioloop import IOLoop
from tornado.queues import Queue

q = Queue(maxsize=2)

@gen.coroutine
def consumer():
    while True:
        item = yield q.get() # 等待获取到q里面的值,如果生产者没有生产,会一直等待。
        try:
            print('Doing work on %s' % item)
            yield gen.sleep(0.01)
        finally:
            q.task_done() # 标记任务完成,如果不标记,那么
                          # q.join() 会从q.queue size() == 0 一直阻塞到timeout

@gen.coroutine
def producer():
    for item in range(5):
        yield q.put(item) # 等待插入对象成功,因为队列长度为2,
                          # 所以,消费者没能及时q.get(),这里就会阻塞
        print('Put %s' % item)

@gen.coroutine
def main():
    # Start consumer without waiting (since it never finishes).
    IOLoop.current().spawn_callback(consumer) # 调用consumer,这里不用等结果,算是异步了,
                                              # 不报错的情况下跟直接用 consumer()差不多
    yield producer()     # 等待producer执行完成——put完所有5个值.这时按理说,q里面有2个对象
    yield q.join(timeout=5)       # 等待q里面的所有对象都被task_done().如果这里不 yield 等待,
                         # 那么producer完成后,程序就退出了,consumer 没有完全消费完
    print('Done')

IOLoop.current().run_sync(main) #运行main函数,main执行完自动结束,不用调用close()
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进