stop(); run_forever()
трюк работает из-за того, как stop
реализуется:
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
Так, в следующий раз, когда запускается цикл обработки событий и выполняет ожидающие обратного вызова, он собирается назвать _raise_stop_error
, что повышает _StopError
. Цикл run_forever
сломается только на конкретном исключением:
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
Так, запланировав stop()
и затем вызвать run_forever
, вы в конечном итоге работает один итерации цикла событий, затем остановка, как только он попадает в _raise_stop_error
обратного вызова. Возможно, вы также заметили, что _run_once
определяется и вызывается run_forever
. Вы можете называть это напрямую, но иногда это может блокироваться, если нет готовых к запуску обратных вызовов, что может быть нежелательно. Я не думаю, что сейчас есть более чистый способ сделать это. Этот ответ был предоставлен Эндрю Светловым, который является участником asyncio
; он, вероятно, знал бы, есть ли лучший вариант.:)
В целом, ваш код выглядит разумным, хотя я думаю, что вы не должны использовать этот подход run_once
для начала. Это не детерминировано; если у вас был более длинный список или более медленная система, для печати всего может потребоваться более двух дополнительных итераций. Вместо этого, вы должны просто послать часовую, который говорит приемнику выключить, а затем ждать как отправлять и получать сопрограммы закончить:
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()
Наконец, asyncio.async
правильный способ добавления задач в цикл обработки событий , create_task
был добавлен в Python 3.4.2, поэтому, если у вас есть более ранняя версия, он не будет существовать.
Я думаю, мне просто очень понравилась идея иметь «полный контроль» в цикле событий, чтобы понять, как это работает в первую очередь. Мне нравится использование SENTINAL, хотя это имеет смысл :) – vitiral