2014-12-28 5 views
14

Я пытаюсь написать сетевую игру с Pygame и asyncio, но я не могу понять, как избежать зависания при чтении. Вот мой код клиента:Неблокирующий ввод-вывод с asyncio

@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 с чертежом и кодом события 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()' выше эмулирует async 'tick()' – jfs

+0

Не будет ли причиной вызова кода pygame (цикл события и сон) для каждого клиента, а не только один раз? – rlms

6

Вы можете «преобразовать» задачу блокировки в неблокируемую.

Предлагаю следующее: https://docs.python.org/3/library/asyncio-eventloop.html#executor.

У меня есть функция, которая слушает твиттер-канал, функцию «упоминать», и я запускаю его в исполнителе, поэтому, если он зависает, он не блокирует другие задачи.

@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

хорошо, так как вы пытаетесь прочитать значение «линии» сразу после вызова чтения(), необходимо это значение любой ценой ...

если сопрограммная не остановит вызвать там нет данных, вы можете получить AttributeError в вызове line.decode(), если «строка» - это None.

одна вещь, которую вы можете сделать, это установить тайм-аут на блокирующий вызов и обрабатывать таймаут исключение:

... 
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()) 
... 
Смежные вопросы