【问题标题】:How can I launch a blocking task from asyncio asynchronously?如何从 asyncio 异步启动阻塞任务?
【发布时间】:2019-07-11 15:24:25
【问题描述】:

我是 asyncio 的新手,我正在尝试制作一个简单的网络服务器,该服务器在收到请求后会计算国际象棋移动并将结果作为响应返回。问题是,该进程被阻塞,使得网络服务器在评估时无法监听和响应请求。我觉得我非常接近,但无法弄清楚接下来的几个步骤以使其正常工作。我已经让国际象棋引擎使用另一个脚本在几个不同的线程中运行,现在我只需要知道如何将它集成到一个简单的 Web 服务器中。对此的任何帮助将不胜感激。这是我的代码:

import asyncio
from aiohttp import web
import chess.engine
from concurrent.futures import ThreadPoolExecutor

import json

engine = chess.engine.SimpleEngine.popen_uci("/usr/games/stockfish")

executor = ThreadPoolExecutor(max_workers=3)


def play():
    global engine
    fen = "rnbqkbnr/pp1ppppp/8/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R b KQkq - 1 2"
    print(fen)
    board = chess.Board(fen)    
    result = engine.play(board,chess.engine.Limit(time=15.0))
    return result

async def run_blocking_task(executor):
    loop = asyncio.get_event_loop()
    result = await loop.run_in_executor(executor, play)
    return result

async def hello(request):
    result = await run_blocking_task(executor)
    return web.Response(text=str(result.move))


app = web.Application()
app.add_routes([web.get('/', hello)])

loop = asyncio.get_event_loop()
handler = app.make_handler()
f = loop.create_server(handler, '0.0.0.0', 8080)
srv = loop.run_until_complete(f)
print('serving on', srv.sockets[0].getsockname())
try:
    loop.run_forever()
except KeyboardInterrupt:
    pass
finally:
    loop.run_until_complete(handler.finish_connections(1.0))
    srv.close()
    loop.run_until_complete(srv.wait_closed())
    loop.run_until_complete(app.finish())
loop.close()

【问题讨论】:

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


    【解决方案1】:

    总体而言,您的代码看起来不错。您遇到的问题是 Python 有限的线程支持。

    尝试将ThreadPoolExecutor 替换为ProcessPoolExecutor (from concurrent.futures import ProcessPoolExecutor),这将在一个单独的进程中运行您的引擎任务,从而允许该任务实际并行运行。

    为了扩展线程问题,Python 解释器有一个锁(称为 GIL),它一次只允许 Python 代码在一个线程上执行。这大大简化了大多数开发任务。锁在特定条件下被释放,调用内核执行 IO(从文件或网络读取)释放 GIL,使 IO 成为线程的良好用例。 ProcessPoolExecutor 在一个单独的进程中执行你的工作任务,它有自己独立的解释器,允许你的代码并行运行,这种方法会带来一些开销,但在现代计算机的上下文中,这是相当小的。 ProcessPoolExecutor会自动为你处理各个进程之间的数据移动过程。

    【讨论】:

    • 感谢您的帮助,蒂姆。这似乎是朝着正确方向迈出的一步——我尝试将 ThreadPoolExecutor 换成 ProcessPoolExecutor,但发生了两件事(或没有发生): 1. 代码仍处于阻塞状态。同时向服务器发送 2 个请求,一个仅在另一个完成后才开始。 2. 客户端没有收到响应。似乎将工作转移到另一个进程使服务器失去了对它应该响应的请求的引用,或者其他什么。不完全确定。
    猜你喜欢
    • 2022-12-21
    • 2021-06-15
    • 1970-01-01
    • 1970-01-01
    • 2017-07-07
    • 2011-05-07
    • 2020-12-21
    • 1970-01-01
    • 2019-01-24
    相关资源
    最近更新 更多