【问题标题】:Non-blocking I/O with asyncio使用 asyncio 的非阻塞 I/O
【发布时间】:2015-02-24 22:27:53
【问题描述】:

我正在尝试使用 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 绘图和事件代码集成?

【问题讨论】:

    标签: python python-3.x pygame python-asyncio


    【解决方案1】:

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

    我建议: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())
    

    【讨论】:

    • 执行器的目的是在单独的线程中运行缓慢或阻塞的操作(进程被内核的调度程序抢占)。 StreamReader.read() 从不阻塞。
    【解决方案2】:

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

    如果协程由于没有数据而无法停止,如果 'line' 为 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())
    ...
    

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      • 谢谢。我是将clock.tick() 调用放在循环中的常用位置,还是需要编写自己的异步调用以避免阻塞网络代码?
      • @sweeneyrod:后者。上面的 sleep() 调用模拟异步 tick()
      • 这不会导致为每个客户端调用 pygame 代码(事件循环和睡眠),而不是只调用一次吗?
      • @sweeneyrod:答案是关于“如何避免在读取时挂起。”应该只有一个 pygame 循环。
      • @Anna:它是 Python 3.4 代码。 async() 函数自 Python 3.5 起已弃用。您可以使用create_task(),或者如果它不可用,则使用ensure_future()docs.python.org/3/library/asyncio-task.html#asyncio.create_task
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多