stop(); run_forever()
sztuczka działa, ponieważ, jak stop
jest realizowany:
def stop(self):
"""Stop running the event loop.
Every callback scheduled before stop() is called will run.
Callback scheduled after stop() is called won't. However,
those callbacks will run if run() is called again later.
"""
self.call_soon(_raise_stop_error)
def _raise_stop_error(*args):
raise _StopError
Więc następnym razem biegnie pętla zdarzenie i wykonuje oczekujące wywołania zwrotne, to będzie zadzwonić _raise_stop_error
, co podnosi _StopError
. Pętla run_forever
złamie tylko na tym konkretnym wyjątkiem:
def run_forever(self):
"""Run until stop() is called."""
if self._running:
raise RuntimeError('Event loop is running.')
self._running = True
try:
while True:
try:
self._run_once()
except _StopError:
break
finally:
self._running = False
Tak, przez planowania stop()
a następnie wywołanie run_forever
, możesz skończyć z systemem jednej iteracji pętli zdarzeń, a następnie zatrzymanie raz natrafi _raise_stop_error
zwrotnego. Być może zauważyłeś także, że _run_once
jest zdefiniowane i wywołane przez run_forever
. Możesz to wywołać bezpośrednio, ale czasami może to być blokowane, jeśli nie ma żadnych uruchomionych wywołań zwrotnych, co może nie być pożądane. Nie sądzę, że jest to obecnie lepszy sposób - Ta odpowiedź została dostarczona przez Andrew Svetlova, który jest współpracownikiem asyncio
; prawdopodobnie wiedziałby, czy jest lepsza opcja.:)
Generalnie Twój kod wygląda rozsądnie, ale myślę, że nie powinieneś używać tego podejścia na początku. To nie jest deterministyczne; jeśli masz dłuższą listę lub wolniejszy system, może to wymagać więcej niż dwóch dodatkowych iteracji, aby wydrukować wszystko. Zamiast tego, należy po prostu wysłać Sentinel, który mówi odbiornik do wyłączenia, a następnie czekać na obu wysyłania i odbierania współprogram skończyć:
import sys
import time
import socket
import asyncio
addr = ('127.0.0.1', 1064)
SENTINEL = b"_DONE_"
# ... (This stuff is the same)
@asyncio.coroutine
def sending(addr, dataiter):
loop = asyncio.get_event_loop()
for d in dataiter:
print("Sending:", d)
sock = socket.socket()
yield from send_close(loop, sock, addr, str(d).encode())
# Send a sentinel
sock = socket.socket()
yield from send_close(loop, sock, addr, SENTINEL)
@asyncio.coroutine
def receiving(addr):
loop = asyncio.get_event_loop()
sock = socket.socket()
try:
sock.setblocking(False)
sock.bind(addr)
sock.listen(5)
while True:
data = yield from accept_recv(loop, sock)
if data == SENTINEL: # Got a sentinel
return
print("Recevied:", data)
finally: sock.close()
def main():
loop = asyncio.get_event_loop()
# add these items to the event loop
recv = asyncio.async(receiving(addr), loop=loop)
send = asyncio.async(sending(addr, range(10)), loop=loop)
loop.run_until_complete(asyncio.wait([recv, send]))
main()
Wreszcie asyncio.async
jest właściwym sposobem na dodanie zadania do pętli zdarzeń . create_task
został dodany w Pythonie 3.4.2, więc jeśli masz wcześniejszą wersję, to nie będzie istnieć.
Sądzę, że po prostu bardzo spodobał mi się pomysł "pełnej kontroli" w pętli zdarzeń, aby zrozumieć, jak to działało w pierwszej kolejności. Podoba mi się użycie SENTINAL, to ma sens :) – vitiral