2014-12-28 82 views
14

我想用Pygame和asyncio編寫一個網絡遊戲,但我無法解決如何避免掛在讀取上。這裏是我的客戶端代碼:asyncio的非阻塞I/O

@asyncio.coroutine 
def handle_client(): 
    print("Connected!") 
    reader, writer = yield from asyncio.open_connection('localhost', 8000) 
    while True: 
     mouse_up = False 
     for event in pygame.event.get(): 
      if event.type == pygame.QUIT: 
       pygame.quit() 
       sys.exit()     
      elif event.type == pygame.MOUSEBUTTONUP: 
       mouse_up = True 

     if mouse_up: 
      print("Writing") 
      writer.write(b"Mouse up") 
     print("Waiting to read") 
     line = yield from reader.read(2**12) 
     print(line.decode()) 

    writer.close() 

這掛就行了line = yield from reader.read(2**12)。我以前認爲asyncio的重點在於它是非阻塞的,所以如果沒有任何數據要讀取,它就會繼續執行。我現在看到情況並非如此。

如何將asyncio網絡代碼與Pygame繪圖和事件代碼集成?

回答

8

yield from的要點是將執行切換到asyncio的事件循環以阻止當前的協同程序,直到結果可用。要在不阻止當前協程的情況下安排任務,可以使用asyncio.async()

要打印讀這麼遠的數據,而不會阻塞pygame的循環:

@asyncio.coroutine 
def read(reader, callback): 
    while True: 
     data = yield from reader.read(2**12) 
     if not data: # EOF 
      break 
     callback(data) 

@asyncio.coroutine 
def echo_client(): 
    reader, ... 
    chunks = [] 
    asyncio.async(read(reader, chunks.append)) 
    while True: 
     pygame.event.pump() # advance pygame event loop 
     ... 
     if chunks: # print read-so-far data 
      print(b''.join(chunks).decode()) 
      del chunks[:] 
     yield from asyncio.sleep(0.016) # advance asyncio loop 

應該有while循環中沒有阻塞調用。

read()sleep()協同程序在同一線程中同時運行(顯然,您也可以同時運行其他協程)。

+0

謝謝。我會把'clock.tick()'調用放在循環中的通常位置,還是我需要編寫自己的異步調用來避免阻塞網絡代碼? – rlms

+1

@sweeneyrod:後者。上面的'sleep()'調用模擬異步'tick()' – jfs

+0

這不會導致爲每個客戶端調用pygame代碼(事件循環和休眠),而不是隻調用一次嗎? – rlms

6

您可以將阻塞任務「轉換」爲非阻塞任務。

我建議這樣做:https://docs.python.org/3/library/asyncio-eventloop.html#executor

我有一個函數,它監聽Twitter提要,函數「提及」,並且我在執行程序中運行它,所以如果它掛起,它不會阻止其他任務。

@asyncio.coroutine 
def boucle_deux(): 
#faire attendre la boucle si pas bcp de mots 
    while True: 
     print("debut du deux") 
     value = t.next() 
     future2 = loop.run_in_executor(None, mention, "LQNyL2xvt9OQMvje7jryaHkN8", 
             "IRJX6S17K44t8oiVGCjrj6XCVKqGSX9ClfpGpfC467rajqePGb", 
             "2693346740-km3Ufby8r9BbYpyzcqwiHhss22h4YkmnPN4LnLM", 
             "53R8GAAncFJ1aHA1yJe1OICfjqUbqwcMR38wSqvbzsQMB", 23, value) 
     response2 = yield from future2 
     yield from asyncio.sleep(5) 
     print("fin du deux") 

asyncio.Task(boucle_deux()) 
+5

執行程序的目的是在單獨的線程中運行緩慢或阻塞操作(該進程被內核的調度程序搶佔)。 StreamReader.read()永遠不會阻塞。 –

0

好,因爲你想讀的「線」你叫讀之後的值(),你需要不惜任何代價,該值...

如果協同程序不會停止導致出現沒有數據,如果'line'是None,你可能會在line.decode()調用中得到一個AttributeError。

一兩件事你可以做的是設置在阻塞調用超時並處理超時異常:

... 
print("Waiting to read") 
try: # block at most for one second 
    line = yield from asyncio.wait_for(reader.read(2**12), 1) 
except asyncio.TimeoutError: 
    continue 
else: 
    print(line.decode()) 
...