Как я могу запустить задачу блокировки из асинхронного асинхронно? - PullRequest
0 голосов
/ 11 июля 2019

Я новичок в asyncio и пытаюсь создать простой веб-сервер, который после получения запроса вычисляет ход в шахматы и возвращает результат в качестве ответа. Проблема в том, что процесс блокируется, поэтому веб-сервер не может прослушивать и реагировать на запросы во время оценки. Я чувствую, что я очень близок, но не могу понять следующие несколько шагов, чтобы сделать эту работу должным образом. Я получил шахматный движок для запуска в нескольких разных потоках с другим скриптом, теперь мне просто нужно знать, как интегрировать это в простой веб-сервер. Любая помощь с этим будет оценена. Вот мой код:

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 голосов
/ 14 июля 2019

В целом ваш код выглядит нормально. Проблема, с которой вы сталкиваетесь, - ограниченная поддержка потоков Pythons.

Попробуйте поменять ThreadPoolExecutor на ProcessPoolExecutor (from concurrent.futures import ProcessPoolExecutor), это запустит задачу вашего движка в отдельном процессе, позволяющем заданию фактически выполняться параллельно.

Чтобы расширить проблему потоков, интерпретатор Python имеет блокировку (известную как GIL), которая позволяет выполнять код Python только в одном потоке за один раз. Это значительно упрощает большинство задач разработки. Блокировка снимается при определенных условиях, при обращении к ядру для выполнения ввода-вывода (при чтении из файла или сети) освобождается GIL, что делает IO хорошим вариантом использования для многопоточности. ProcessPoolExecutor выполняет вашу рабочую задачу в отдельном процессе со своим собственным отдельным интерпретатором, позволяющим выполнять ваш код параллельно, этот подход сопряжен с некоторыми накладными расходами, но в контексте современных компьютеров это довольно минимально. ProcessPoolExecutor автоматически обрабатывает процесс перемещения данных между вами.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...