2017-05-26 51 views
1

當我在簡單的Tornado應用程序中運行此處理程序並使用curl發出兩個請求時,它不會並行運行。當我想要打印「1 1 2 2 3 3 4 4 5 5」時,打印出「1 2 3 4 5 1 2 3 4 5」。爲什麼在Tornado協同程序中time.sleep並行運行?

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

我在做什麼錯了?

回答

3

這樣做的原因是,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) 
    

    參見龍捲風用戶指南的Asynchronous I/O chapter更多關於阻塞和異步函數。

+0

我希望你能粘這個答案 –

+0

這就是主意。這是複製[龍捲風常見問題](http://www.tornadoweb.org/en/stable/faq.html#why-isn-t-this-example-with-time-sleep-running-in-parallel)因此所有其他提出這個問題的問題都可以作爲這個問題的重複來解決。 –

相關問題