この記事は Pythonのコードを短く簡潔に書くテクニック Advent Calendar 2017 の19日目です。
はじめに
Pythonでスレッドベースの並列処理をするには以前はThreadを使うしかありませんでしたが、3.2からはconcurrent.futuresモジュールが追加され、マルチタスク処理がやりやすくなりました。
例題
引数で受け取った値を返すだけの関数を複数実行するケースを考えてみます。
import random
import time
def task(value):
print('{} start'.format(value))
time.sleep(random.uniform(0.5, 1.0))
print('{} end'.format(value))
return value
Threadを使う場合
3.1まではこういう書き方をするしかありませんでした。
from threading import Thread
results = []
f = lambda v: results.append(task(v))
threads = [Thread(target=f, args=(i,)) for i in range(10)]
for thread in threads:
thread.start()
for thread in threads:
thread.join()
print(results)
- Threadで呼び出す関数から直接戻り値は取得できないので、lambda式で戻り値を
results
に格納します。 - Threadを作成しただけでは実行されないので、
start()
を呼びます。 - Threadの完了を待つために
join()
を呼びます。
メンドくさいですね。
concurrent.futures.ThreadPoolExecutorを使う場合
from concurrent.futures import ThreadPoolExecutor
with ThreadPoolExecutor(max_workers=10) as executor:
results = list(executor.map(task, range(10)))
print(results)
concurrent.futures.ThreadPoolExecutorを使うと、スレッドベースのマルチタスク処理を実行できます。
map()は第1引数の関数に対して第2引数の要素を順番に渡した呼び出しを行ってくれます。
戻り値は関数の実行結果のイテレータになります。
参考
- Python3ドキュメント
- ライブラリーリファレンス
- Pythonでconcurrent.futuresを使った並列タスク実行 - Qiita