如何从asyncio异步启动阻止任务?

时间:2019-07-11 15:24:25

标签: python python-3.x asyncio

我是asyncio的新手,正尝试制作一个简单的Web服务器,该服务器在收到请求后会计算下棋动作并将结果作为响应返回。问题是,进程正在阻塞,从而使Web服务器在评估时无法侦听请求并对请求做出反应。我觉得我已经很接近了,但是无法弄清楚接下来的几个步骤才能使这项工作正常进行。我已经使国际象棋引擎可以在另一个脚本中的多个不同线程中运行,现在我只需要知道如何将其集成到简单的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()

1 个答案:

答案 0 :(得分:0)

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

尝试将ThreadPoolExecutor换成ProcessPoolExecutorfrom concurrent.futures import ProcessPoolExecutor),这将在单独的进程中运行引擎任务,从而使该任务实际并行运行。

为扩展线程问题,Python解释器具有一个锁(称为GIL),该锁仅允许一次在一个线程上执行Python代码。这大大简化了大多数开发任务。在某些情况下释放该锁,调用内核执行IO(从文件或网络读取)将释放GIL,从而使IO成为线程化的良好用例。 ProcessPoolExecutor在一个单独的进程中使用其自己的单独的解释器执行您的工作者任务,从而使您的代码可以并行运行,这种方法会带来一些开销,但是在现代计算机的情况下,这是相当少的。 ProcessPoolExecutor将自动为您处理在每个过程之间移动数据的过程。