2013-10-31 102 views
3

我有一个需要一段时间才能运行的功能,需要在请求中运行它。处理这个问题的最好方法是什么,以便这个请求在处理时不会阻塞主线程?我看了一下@tornado.web.asynchronous装饰器,但是当这个函数不是异步龙卷风模块时,这里没什么用处。龙卷风无阻塞请求

class LongHandler(tornado.web.RequestHandler): 
    def get(self): 
     self.write(self.long_time_function()) 

    def long_time_function(self): 
     time.sleep(5) 
     return "foo" 

回答

7

当你有一些阻塞的任务,不玩异步事件循环不错,你必须把它放在一个单独的线程。

如果您打算有无限数量的阻塞任​​务,您希望使用线程池。

无论采用哪种方式,您都希望有一个封装异步任务,阻止来自线程化任务的通知。

要做到这一点,最简单的方法是使用像tornado-threadpool预建图书馆*然后,你就做这样的事情:

class LongHandler(tornado.web.RequestHandler): 
    @thread_pool.in_thread_pool 
    def long_time_function(self, callback): 
     time.sleep(5) 
     callback("foo") 

如果你想自己做,this gist展示了一个例子你必须做什么 - 或者当然,各种Tornado线程池库的源代码可以作为示例代码。只要记住Python GIL的局限性:如果你的后台任务是CPU绑定的(并且在Python中执行大部分工作,而不是像释放GIL那样的C扩展),你必须把它放在一个单独的过程中。龙卷风进程池库中快速搜索不转了许多不错的选择,但适应线程池代码来处理池的代码通常是在Python非常容易。**


*请注意,我不是特别推荐该图书馆;这只是Google搜索中出现的第一件事情,并且快速浏览它看起来可用且正确。

**它通常很简单,只要用concurrent.futures.ProcessPoolExecutormultiprocessing.dummy.Pool替换就可以用multiprocessing.Pool替换。唯一的技巧是确保所有的任务参数和返回值都很小并且可以选择。

1

使用另一个线程如其他答案中所述是一种方法。如果你的函数可以被分割成片段(例如,里面有很多迭代的循环),你也可以使用IOLoop.add_callback()来分割它以交织处理其他请求。这里是一个例子,如何做到这一点:why my coroutine blocks whole tornado instance?

+0

谢谢,我看到另一个类似的策略问题/答案,但在这种情况下,我无法控制功能如何工作;我只是叫它。 –