Python
python3

Python3.2以降でのマルチタスク処理はThreadではなくconcurrent.futuresを使え

この記事は 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引数の要素を順番に渡した呼び出しを行ってくれます。
戻り値は関数の実行結果のイテレータになります。

参考