2016-05-14 43 views
3

我有一個情況,我有一個「服務器」線程應該監聽來自其他服務器線程的調用/事件,同時執行一些其他代碼。最近我在Node.js上做了很多工作,所以我認爲使用async/await創建一個事件循環會很好,我可以等待其他線程加入事件循環,並在最終加入時處理它們的響應。Python asyncio等待線程

爲了驗證這一想法我在Python 3.5寫了下面的測試腳本:

# http://stackabuse.com/python-async-await-tutorial/ 
# Testing out Python's asynchronous features 
import asyncio 
from time import sleep 
import threading 
from threading import Thread 
import random 

class MyThread(Thread): 

    def __init__(self, message): 
     Thread.__init__(self) 
     self._message = message 

    def run(self): 
     self._return = self._message + " oli viesti" 
     a = random.randint(1, 5) 
     print("Sleep for ", a) 
     sleep(a) 
     print("Thread exiting...") 


    def join(self): 
     Thread.join(self) 
     return self._return 



async def send(message): 
    t = MyThread(message) # daemon = True 
    t.start() 
    print("asd") 
    return t.join() 

async def sendmsg(msg): 
    response = await send(msg) 
    print("response is ", response) 


if __name__ == "__main__": 
    # Initiate a new thread and pass in keyword argument dictionary as parameters 
    loop = asyncio.get_event_loop() 
    tasks = [ 
     asyncio.ensure_future(sendmsg("hippa1"), loop=loop), 
     asyncio.ensure_future(sendmsg("hippa2"), loop=loop), 
     asyncio.ensure_future(sendmsg("hippa3"), loop=loop) 
    ] 

    loop.run_until_complete(asyncio.wait(tasks)) 
    loop.close() 

在我想踢了與不同的字符串三個工作線程,並等待他們完成的例子。工作人員睡眠的時間是隨機的,因此當腳本運行多次時,我希望他們按隨機順序完成。原來,他們似乎順序執行,第二個線程開始之後。

我在這裏有什麼錯誤?不應該睡覺只阻擋它所在的線程嗎?我的事件循環是否設置正確?我可以異步/等待連接嗎?

最終,我想發送消息到其他線程,並等待他們的迴應,然後運行帶有返回值的回調函數。

編輯:澄清,最終我想等待在我的主線程中的異步/等待條件變量,並運行其他代碼,直到某些條件變量讓執行通過。在這個示例代碼中,我試圖對工作線程的連接做同樣的事情。

+0

'time.sleep'不是異步的,可以用'await asyncio.sleep'嘗試它 – jonrsharpe

+0

但是它是在一個單獨的線程中觸發的,所以不應該只是阻塞單獨的線程而不是主線程的事件循環是什麼?我的理解是,睡眠是線程,而不是過程阻塞。那麼,爲什麼連接我的主線程,即使是異步/等待結構? – Tumetsu

回答

1

最終,它是運行的,因爲這代碼順序:

async def send(message): 
    t = MyThread(message) # daemon = True 
    t.start() 
    print("asd") 
    return t.join() 

你啓動一個線程,然後立即等待線程完成,然後再繼續。這就是爲什麼他們順序執行。

Node.js和asyncio不一定會創建新線程來執行其操作。例如,Node.js只使用一個線程,但它使用內核級函數(例如'epoll')來調用您在發生某些新網絡活動時指示的回調。這允許單個線程管理數百個網絡連接。

這就是爲什麼當你在沒有Thread實例的情況下執行它時,你會在當前正在運行的線程上調用sleep,這與主線程相同。當你在網絡功能中使用asyncio時,你可以使用「yield from」結構,這允許其他代碼塊執行,而其他任務正在處理其他遠程服務。

主要結構是正確的。你想要的代碼塊:

loop.run_until_complete(asyncio.wait(tasks)) 

但不依賴於「睡眠」測試功能,您需要一個網絡電話,或使用:

yield from asyncio.sleep(1) 

而且也沒有必要在這種情況下啓動單獨的線程。

+0

關於線程啓動的好處。然而,在我的情況下,我真的需要單獨的線程,但我想*如果可能的話,等待它們異步的條件。所以我想有一個函數檢查事件循環中的連接,但是如果仍然執行其他任意代碼,直到在循環中解除阻塞爲止。所以我的情況並不完全類似於Node.js,我反而希望等待其他線程完成/設置條件變量,而不使用異步事件循環阻塞主線程。 – Tumetsu

+0

總之我無法弄清楚如何寫這樣的東西: yield t.join() – Tumetsu

+0

在這種情況下,我建議你根本不要使用asyncio,但是要使用中間的隊列。您可以使用task_done()等待隊列並使用get()來獲取工作項目。然後您可以使用另一個隊列將結果傳回主線程。有一個代碼示例顯示在文檔頁面上:https://docs.python.org/3/library/queue.html – radialmind