Je veux exécuter un service qui demande des URL en utilisant des coroutines et du multithread. Cependant, je ne peux pas transmettre de coroutines aux travailleurs de l'exécuteur testamentaire. Consultez le code ci-dessous pour un exemple minimal de ce problème:
import time
import asyncio
import concurrent.futures
EXECUTOR = concurrent.futures.ThreadPoolExecutor(max_workers=5)
async def async_request(loop):
await asyncio.sleep(3)
def sync_request(_):
time.sleep(3)
async def main(loop):
futures = [loop.run_in_executor(EXECUTOR, async_request,loop)
for x in range(10)]
await asyncio.wait(futures)
loop = asyncio.get_event_loop()
loop.run_until_complete(main(loop))
Il en résulte l'erreur suivante:
Traceback (most recent call last):
File "co_test.py", line 17, in <module>
loop.run_until_complete(main(loop))
File "/usr/lib/python3.5/asyncio/base_events.py", line 387, in run_until_complete
return future.result()
File "/usr/lib/python3.5/asyncio/futures.py", line 274, in result
raise self._exception
File "/usr/lib/python3.5/asyncio/tasks.py", line 239, in _step
result = coro.send(None)
File "co_test.py", line 10, in main
futures = [loop.run_in_executor(EXECUTOR, req,loop) for x in range(10)]
File "co_test.py", line 10, in <listcomp>
futures = [loop.run_in_executor(EXECUTOR, req,loop) for x in range(10)]
File "/usr/lib/python3.5/asyncio/base_events.py", line 541, in run_in_executor
raise TypeError("coroutines cannot be used with run_in_executor()")
TypeError: coroutines cannot be used with run_in_executor()
Je sais que je pourrais utiliser sync_request
funcion au lieu de async_request
, dans ce cas, j'aurais des coroutines en envoyant la fonction de blocage à un autre thread.
Je sais aussi que je pourrais appeler async_request
dix fois dans la boucle d'événement. Quelque chose comme dans le code ci-dessous:
loop = asyncio.get_event_loop()
futures = [async_request(loop) for i in range(10)]
loop.run_until_complete(asyncio.wait(futures))
Mais dans ce cas, j'utiliserais un seul thread.
Comment pourrais-je utiliser les deux scénarios, les coroutines fonctionnant dans les multithreads? Comme vous pouvez le voir par le code, je passe (et n'utilise pas) le pool
au async_request
dans l'espoir que je puisse coder quelque chose qui dit au travailleur de faire un futur, l'envoyer au pool et de manière asynchrone (libérer le travailleur) attend le résultat.
La raison pour laquelle je veux le faire est de rendre l'application évolutive. Est-ce une étape inutile? Dois-je simplement avoir un thread par URL et c'est tout? Quelque chose comme:
LEN = len(list_of_urls)
EXECUTOR = concurrent.futures.ThreadPoolExecutor(max_workers=LEN)
est assez bon?
Vous devez créer et définir une nouvelle boucle d'événements dans le contexte du thread afin d'exécuter des coroutines:
import asyncio
from concurrent.futures import ThreadPoolExecutor
def run(corofn, *args):
loop = asyncio.new_event_loop()
try:
coro = corofn(*args)
asyncio.set_event_loop(loop)
return loop.run_until_complete(coro)
finally:
loop.close()
async def main():
loop = asyncio.get_event_loop()
executor = ThreadPoolExecutor(max_workers=5)
futures = [
loop.run_in_executor(executor, run, asyncio.sleep, 1, x)
for x in range(10)]
print(await asyncio.gather(*futures))
# Prints: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main())