为什么time.sleep在Tornado协程中并行运行?

时间:2017-05-26 15:21:11

标签: asynchronous tornado

当我在一个简单的Tornado应用程序中运行此处理程序并使用 // Transition definition const FadeTransition = (index, position) => { const inputRange = [index - 1, index, index + 1]; const opacity = position.interpolate({ inputRange, outputRange: [0, 1, 1], }); return { opacity }; }; // Transition configurator const TransitionConfiguration = () => { return { screenInterpolator: (sceneProps) => { const {position, scene} = sceneProps; const {index, route} = scene; return FadeTransition(index, position); } } }; // Active scenes const appScenes = { Demo: { screen: Demo }, Foo: { screen: Foo } }; // Basic stack navigator import { StackNavigator } from 'react-navigation'; const AppNavigator = StackNavigator(appScenes, { headerMode: 'none', transitionConfig: TransitionConfiguration }); import { NavigationActions } from 'react-navigation'; this.props.navigate({ routeName: 'Foo', params: { transition: 'fade' } }) 向它发出两个请求时,它并不是并行运行的。打印出来" 1 2 3 4 5 1 2 3 4 5",当我想要打印" 1 1 2 2 3 3 4 4 5 5"。

curl

我做错了什么?

1 个答案:

答案 0 :(得分:4)

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

当然,time.sleep通常只是这些示例中的占位符,重点是显示处理程序中的某些内容变慢时会发生什么。无论真正的代码在做什么,要实现并发阻塞代码必须用非阻塞等价物替换。这意味着以下三点之一:

  • 找到一个兼容协程的。对于time.sleep,请改用tornado.gen.sleep:

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

    当此选项可用时,通常是最佳方法。有关可能有用的异步库的链接,请参阅Tornado wiki

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

    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可用于查找合适的库。

  • 在另一个线程上运行阻止代码。当异步库不可用时,concurrent.futures.ThreadPoolExecutor可用于在另一个线程上运行任何阻塞代码。这是一种通用解决方案,无论是否存在异步对应物,都可以用于任何阻塞函数:

    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用户指南的Asynchronous I/O chapter