2017-02-16 62 views
4

我有一個協同程序如下:Syncronous睡眠到ASYNCIO協程

async def download(): 
    downloader = DataManager() 
    downloader.download() 

DataManager.download()方法是這樣的:

def download(self): 
    start_multiple_docker_containers() 
    while True: 
     check_containers_statuses() 
     sleep(N) # synchronous sleep from time module 

這是一個好的做法呢?如果不是,我怎樣才能在download()中使用asyncio.sleep

或者也許這樣的代碼結構在概念上是錯誤的?

+1

你能'DataManager.download'協同程序? – dirn

+0

當然可以。但是,如果我想用不同的方法分解代碼,則必須將它們作爲協程。如果下載任務太多,恐怕會對性能產生負面影響。 –

回答

2

這裏是我的解決方案:

import asyncio 
import time 


# Mocks of domain-specific functions 
# ---------------------------------- 

def get_container_status(container_id, initial_time): 
    """This mocks container status to change to 'exited' in 10 seconds""" 
    if time.time() - initial_time < 10: 
     print("%s: container %s still running" % (time.time(), container_id)) 
     return 'running' 
    else: 
     print("%s: container %s exited" % (time.time(), container_id)) 
     return 'exited' 

def is_download_complete(container_id, initial_time): 
    """This mocks download finished in 20 seconds after program's start""" 
    if time.time() - initial_time < 20: 
     print("%s: download from %s in progress" % (time.time(), container_id)) 
     return False 
    else: 
     print("%s: download from %s done" % (time.time(), container_id)) 
     return True 

def get_downloaded_data(container_id): 
    return "foo" 


# Coroutines 
# ---------- 

async def container_exited(container_id, initial_time): 
    while True: 
     await asyncio.sleep(1) # == setTimeout(1000), != sleep(1000) 
     if get_container_status(container_id, initial_time) == 'exited': 
      return container_id 

async def download_data_by_container_id(container_id, initial_time): 
    container_id = await container_exited(container_id, initial_time) 
    while True: 
     await asyncio.sleep(1) 
     if is_download_complete(container_id, initial_time): 
      return get_downloaded_data(container_id) 


# Main loop 
# --------- 

if __name__ == "__main__": 

    initial_time = time.time() 

    loop = asyncio.get_event_loop() 

    tasks = [ 
     asyncio.ensure_future(download_data_by_container_id("A", initial_time)), 
     asyncio.ensure_future(download_data_by_container_id("B", initial_time)) 
    ] 

    loop.run_until_complete(asyncio.wait(tasks)) 

    loop.close() 

結果:

1487334722.321165: container A still running 
1487334722.321412: container B still running 
1487334723.325897: container A still running 
1487334723.3259578: container B still running 
1487334724.3285959: container A still running 
1487334724.328662: container B still running 
1487334725.3312798: container A still running 
1487334725.331337: container B still running 
1487334726.3340318: container A still running 
1487334726.33409: container B still running 
1487334727.336779: container A still running 
1487334727.336842: container B still running 
1487334728.339425: container A still running 
1487334728.339506: container B still running 
1487334729.34211: container A still running 
1487334729.342168: container B still running 
1487334730.3448708: container A still running 
1487334730.34493: container B still running 
1487334731.34754: container A exited 
1487334731.347598: container B exited 
1487334732.350253: download from A in progress 
1487334732.3503108: download from B in progress 
1487334733.354369: download from A in progress 
1487334733.354424: download from B in progress 
1487334734.354686: download from A in progress 
1487334734.3548028: download from B in progress 
1487334735.358371: download from A in progress 
1487334735.358461: download from B in progress 
1487334736.3610592: download from A in progress 
1487334736.361115: download from B in progress 
1487334737.363115: download from A in progress 
1487334737.363211: download from B in progress 
1487334738.3664992: download from A in progress 
1487334738.36656: download from B in progress 
1487334739.369131: download from A in progress 
1487334739.36919: download from B in progress 
1487334740.371079: download from A in progress 
1487334740.37119: download from B in progress 
1487334741.374521: download from A done 
1487334741.3745651: download from B done 

至於sleep()功能 - 不,你不應該使用它。它阻塞了整個python解釋器1秒鐘,這不是你想要的。

請記住,你沒有並行(線程等),你有併發

I.e.你有一個只有1個執行線程的Python解釋器,你的主循環和所有的協程運行,相互搶佔。您希望您的解釋器在該主循環中花99.999%的工作時間,由asyncio創建,輪詢套接字並等待超時。

所有的協程都應該儘可能快地返回,絕對不應該包含阻止sleep - 如果你調用它,它會阻塞整個解釋器並阻止主循環從套接字獲取信息或響應數據運行協程,到達到那些插座。

因此,您應該等待asyncio.sleep(),它基本上等同於Javascript的setTimeout() - 它只是告訴主循環,在特定時間內它應該喚醒此協程並繼續運行它。


推薦閱讀:

3

這很可能是一種不好的做法,因爲time.sleep()會阻塞所有內容,而您只想阻塞特定的協程(我猜)。

您正在異步世界中進行同步操作。

以下模式如何?

async def download(): 
    downloader = DataManager() 
    downloader.start_multiple_docker_containers() 
    while True: 
     downloader.check_containers_statuses() 
     await syncio.sleep(N) 
+0

它工作正常。不知道爲什麼,但在昨天晚上,代碼如下: async def some1(): await some2(); async def some2(): await someN() 看起來沒有什麼特別之處。 謝謝! –