为什么time.sleep在龙卷风协程中不并行运行? [英] Why doesn't time.sleep run in parallel in a Tornado coroutine?

查看:178
本文介绍了为什么time.sleep在龙卷风协程中不并行运行?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当我在一个简单的Tornado应用程序中运行此处理程序并使用curl对其发出两个请求时,它不会并行运行.当我希望打印"1 1 2 2 3 3 4 4 5 5"时,它会打印出"1 2 3 4 5 1 2 3 4 5".

When I run this handler in a simple Tornado app and make two requests to it with curl, it doesn't run in parallel. It prints out "1 2 3 4 5 1 2 3 4 5", when I want it to print "1 1 2 2 3 3 4 4 5 5".

class SleepHandler(RequestHandler):
    def get(self):
        for i in range(5):
            print(i)
            time.sleep(1)

我在做什么错了?

推荐答案

原因是time.sleep blocking 函数:它不允许控件返回到,以便可以运行其他处理程序.

The reason for this is that time.sleep is a blocking function: it doesn’t allow control to return to the IOLoop so that other handlers can be run.

当然,在这些示例中,time.sleep通常只是一个占位符,重点是要显示当处理程序中的某些东西变慢时会发生什么.无论实际代码在做什么,要实现并发性,阻塞代码都必须替换为非阻塞等效项.这意味着三件事之一:

Of course, time.sleep is often just a placeholder in these examples, the point is to show what happens when something in a handler gets slow. No matter what the real code is doing, to achieve concurrency blocking code must be replaced with non-blocking equivalents. This means one of three things:

  • 找到一个协程友好的等效项.对于time.sleep,请改用tornado.gen.sleep:

  • Find a coroutine-friendly equivalent. For time.sleep, use tornado.gen.sleep instead:

class CoroutineSleepHandler(RequestHandler):
    @gen.coroutine
    def get(self):
        for i in range(5):
            print(i)
            yield gen.sleep(1)

此选项可用时,通常是最佳方法.有关可能有用的异步库的链接,请参见 Tornado Wiki .

When this option is available, it is usually the best approach. See the Tornado wiki for links to asynchronous libraries that may be useful.

查找基于回调的等效项.与第一个选项类似,基于回调的库可用于许多任务,尽管它们的使用比为协程设计的库稍微复杂一些.这些通常与tornado.gen.Task一起用作适配器:

Find a callback-based equivalent. Similar to the first option, callback-based libraries are available for many tasks, although they are slightly more complicated to use than a library designed for coroutines. These are typically used with tornado.gen.Task as an adapter:

class CoroutineTimeoutHandler(RequestHandler):
    @gen.coroutine
    def get(self):
        io_loop = IOLoop.current()
        for i in range(5):
            print(i)
            yield gen.Task(io_loop.add_timeout, io_loop.time() + 1)

同样, Tornado Wiki 可以用来找到合适的库.

Again, the Tornado wiki can be useful to find suitable libraries.

在另一个线程上运行阻塞代码.当异步库不可用时,concurrent.futures.ThreadPoolExecutor可用于在另一个线程上运行任何阻塞代码.这是一种通用解决方案,无论是否存在异步对应项,均可用于任何阻塞功能:

Run the blocking code on another thread. When asynchronous libraries are not available, concurrent.futures.ThreadPoolExecutor can be used to run any blocking code on another thread. This is a universal solution that can be used for any blocking function whether an asynchronous counterpart exists or not:

executor = concurrent.futures.ThreadPoolExecutor(8)

class ThreadPoolHandler(RequestHandler):
    @gen.coroutine
    def get(self):
        for i in range(5):
            print(i)
            yield executor.submit(time.sleep, 1)

请参见Tornado用户的异步I/O章节有关阻塞和异步功能的更多指南.

See the Asynchronous I/O chapter of the Tornado user’s guide for more on blocking and asynchronous functions.

这篇关于为什么time.sleep在龙卷风协程中不并行运行?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆