I\'m trying to write a networked game with Pygame and asyncio, but I can\'t work out how to avoid hanging on reads. Here is my code for the client:
@asyncio.coro
The point of yield from
is to switch the execution to the asyncio's event loop and to block the current coroutine until the result is available. To schedule a task without blocking the current coroutine, you could use asyncio.async()
.
To print read-so-far data without blocking the pygame loop:
@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
There should be no blocking calls inside the while
loop.
read()
and sleep()
coroutines run concurrently in the same thread (obviously you could run other coroutines concurrently too).
well since you are trying to read the value of 'line' right after you call read() you need that value at any cost...
if the coroutine wouldn't stop cause there are no data, you could get an AttributeError on the line.decode() call if 'line' then is None.
one thing you can do is to set a timeout on the blocking call and handle the timeout exception:
...
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())
...
You can "transform" a blocking task into a non-blocking one.
I suggest this: https://docs.python.org/3/library/asyncio-eventloop.html#executor.
I have a function that listens to a twitter feed, function "mention", and I run it in an executor, so if it hangs, it doesn't block the other tasks.
@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())