コルーチンとマルチスレッドを使用してURLを要求するサービスを実行したい。ただし、エグゼキュータのワーカーにコルーチンを渡すことはできません。この問題の最小限の例については、以下のコードを参照してください。
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))
次のエラーが発生します。
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()
私はsync_request
の代わりにasync_request
関数を使用できることを知っています。この場合、ブロッキング関数を別のスレッドに送信することによってコルーチンを持つことになります。
また、イベントループでasync_request
を10回呼び出すことができることも知っています。以下のコードのようなもの:
loop = asyncio.get_event_loop()
futures = [async_request(loop) for i in range(10)]
loop.run_until_complete(asyncio.wait(futures))
ただし、この場合は単一のスレッドを使用します。
マルチスレッド内で機能するコルーチンを両方のシナリオでどのように使用できますか?コードを見るとわかるように、私はpool
をasync_request
に渡しています(使用していません)。これは、ワーカーに未来を作ってそれをプールし、非同期で(ワーカーを解放して)結果を待ちます。
私がそうしたいのは、アプリケーションをスケーラブルにするためです。不要なステップですか?私は単にURLごとにスレッドを持っているべきですか?それはそれですか?何かのようなもの:
LEN = len(list_of_urls)
EXECUTOR = concurrent.futures.ThreadPoolExecutor(max_workers=LEN)
十分ですか?
コルーチンを実行するには、スレッドコンテキストで新しいイベントループを作成して設定する必要があります。
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())