概要
いつ使うんだと言われてしまうPythonのスレッドですが、Pythonの外で遅い原因があるときは高速化に威力を発揮します。
たとえばこんな感じです。言語はbashです。
#!/bin/bash sleep 3 echo "hoge"
特にひねりはありません。slow_command.shとでもして保存しておきます。
Pythonから呼ぶと、
import subprocess for _ in range(5): result = subprocess.run(["./slow_command.sh"], stdout=subprocess.PIPE) print(result.stdout.decode())
いい加減ですがこんな感じでしょうか。当たり前のように15秒かけて実行してくれます。
ThreadPoolExecutorを使う
- CPUバウンドではない
- 遅いのはPythonではない
といった条件が揃っているので、Pythonのスレッドを使う良い機会です。
threading直叩きはこの場合つらいものがあるので、concurrent.futures.ThreadPoolExecutorを使います。
concurrent.futures -- 並列タスク実行 — Python 3.8.2 ドキュメント
import subprocess from concurrent.futures import ThreadPoolExecutor def f(): result = subprocess.run(["./slow_command.sh"], stdout=subprocess.PIPE) return result.stdout.decode() with ThreadPoolExecutor(max_workers=5) as pool: futures = [pool.submit(f) for _ in range(5)] for f in futures: print(f.result())
今度はちゃんと5秒で終わります。
asyncio
asyncioはこれまでとっつきづらい気がしてほとんど触ってこなかったのですが、やればできるんじゃないでしょうか。
python - How to use asyncio with existing blocking library? - Stack Overflow
まだ試していないので、そのうち試してみて記事をアップデートします(か、別記事になるかもしれない)。
使いみち
遅いサブプロセスの結果を大量に受けるときはありです。
スレッド並列といってもけっきょく子プロセスが生えてしまうので、ワーカーの数には気をつけた方が良いでしょう(同時に子プロセス生やしすぎてメモリエラー、とか)。