2013-10-31 134 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

謝謝,我看到另一個類似的策略問題/答案,但在這種情況下,我無法控制功能如何工作;我只是叫它。 –