26

我正在尝试使用 Pygame 和 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 的意义在于它是非阻塞的,因此如果没有任何数据要读取,它就会继续执行。我现在看到情况并非如此。

如何将 asyncio 网络代码与 Pygame 绘图和事件代码集成?

4

3 回答 3

14

的重点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()协程在同一个线程中同时运行(显然你也可以同时运行其他协程)。

于 2014-12-29T00:49:56.313 回答
10

您可以将阻塞任务“转换”为非阻塞任务。

我建议这样做:https ://docs.python.org/3/library/asyncio-eventloop.html#executor 。

我有一个监听 twitter 提要的函数,函数“提及”,我在执行程序中运行它,所以如果它挂起,它不会阻塞其他任务。

@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())
于 2015-01-03T17:32:39.003 回答
0

好吧,因为您在调用 read() 后立即尝试读取“line”的值,所以不惜一切代价需要该值...

如果协程不会因为没有数据而停止,如果 'line' then 为 None,你可能会在 line.decode() 调用上得到一个 AttributeError。

您可以做的一件事是在阻塞调用上设置超时并处理超时异常:

...
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())
...
于 2015-06-15T18:04:06.263 回答