Nieblokujące we / wy z asyncio

[[2]] staram się napisać grę sieciową z Pygame i asyncio, ale nie potrafię wymyślić, jak uniknąć zawieszania się na czytaniu. Oto Mój kod dla klienta:

@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()

To wisi na linii line = yield from reader.read(2**12). Wcześniej myślałem, że asyncio chodzi o to, że nie blokuje, a więc jeśli nie ma żadnych danych do odczytu, to po prostu kontynuuje wykonywanie. Teraz widzę, że tak nie jest.

Jak zintegrować kod sieciowy asyncio z rysunkiem Pygame i eventem kod?

Author: rlms, 2014-12-28

3 answers

Punktem yield fromjest przełączenie wykonania na pętlę zdarzeń asyncio i, aby zablokować bieżącą koronę, dopóki wynik nie będzie dostępny. Aby zaplanować zadanie bez blokowania bieżącego coroutine, możesz użyć asyncio.async().

Aby wydrukować dane read-so-far bez blokowania pętli 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

Nie powinno być blokowania połączeń wewnątrz pętli while.

read() i sleep() koroutiny działają jednocześnie w tym samym wątku (oczywiście można uruchomić inne koroutiny jednocześnie też).

 12
Author: jfs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-29 11:50:18

Możesz "przekształcić" zadanie blokujące w nieblokujące.

Proponuję: https://docs.python.org/3/library/asyncio-eventloop.html#executor .

Mam funkcję, która nasłuchuje kanału na Twitterze, funkcję "wzmiankę" i uruchamiam ją w executorze, więc jeśli się zawiesi, nie blokuje innych zadań.

@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())
 7
Author: Morgan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-03 17:25:18

Cóż, ponieważ próbujesz odczytać wartość 'line' zaraz po wywołaniu read (), potrzebujesz tej wartości za wszelką cenę...

Jeśli coroutine nie zatrzyma się, ponieważ nie ma danych, możesz uzyskać AttributeError na linii.decode () wywołanie if 'line' then is None.

Jedną z rzeczy, które możesz zrobić, to ustawić limit czasu na wywołanie blokujące i obsłużyć wyjątek timeout:

...
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())
...
 0
Author: Danny,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-15 18:04:06