pythonでマルチスレッドプログラミング

pythonでマルチスレッドプログラミング

pythonは簡単にマルチスレッドで動作するプログラムを簡単に作れる。
javaだと、それ用にクラスを作成する必要があるが、pythonでは関数をマルチスレッドで動かすことができる。
ちょっと作ってみたツールが、性能的に問題がある場合、該当処理を関数に切り出し、それをマルチスレッドで動かせる。

とりあえずプログラムの全貌

[cc lang=”python”]
#!/usr/bin/env python

import threading
import queue
import logging

def worker(q):
”’pythonでキューを多重処理するスレッド”’
while True:
item = q.get()
if item is None:
break
logging.info(item)

”’どのスレッドで処理されているかどうか見たいので、logging機能を使ってログ出力する”’
logging.basicConfig(
format = ‘%(asctime)s [%(levelname)-5s] [%(thread)5d] (%(name)s) %(message)s’,
level = ‘INFO’)
”’開始メッセージを出力”’
logging.info(‘start’)
”’処理キューを登録するためのqueueを作成する”’
q = queue.Queue()
”’キューを処理するスレッドを準備する”’
threads = []
for i in range(20):
t = threading.Thread(target = worker, args = (q, ))
t.start()
threads.append(t)
”’キューにタスクを登録する”’
for item in range(100):
q.put(item)
”’スレッドの数だけ、キュー終端のNoneをキューに登録する”’
for t in threads:
q.put(None)
”’スレッドが終了することを待ち合わせる”’
for t in threads:
t.join()
”’終了メッセージを出力”’
logging.info(‘end’)
[/cc]

処理をする関数を作成する

[cc lang=”python”]
def worker(q):
”’pythonでキューを多重処理するスレッド”’
while True:
item = q.get()
if item is None:
break
logging.info(item)
[/cc]
while Trueの無限ループを作成しておき、queueからキューを1つポップする。
キューからポップした値がNoneのときはループを抜ける。
キューの最後にNoneを入れるのは、後のキュー登録の後処理で行う。

検証用にloggingを準備する

[cc lang=”python”
logging.basicConfig(
format = ‘%(asctime)s [%(levelname)-5s] [%(thread)5d] (%(name)s) %(message)s’,
level = ‘INFO’)
[/cc]

loggingを利用するとログにスレッドのIDを出力できるようになる。
これを利用して、マルチスレッドで処理されていることを確認する。

空のキューを用意し、マルチスレッドで動作させるスレッドを用意する

[cc lang=”python”]
q = queue.Queue()
”’キューを処理するスレッドを準備する”’
threads = []
for i in range(20):
t = threading.Thread(target = worker, args = (q, ))
t.start()
threads.append(t)
[/cc]

先に空のキューを作成しておく。
そのキューを引数に取る、マルチスレッドで実行するスレッドを作成する。
実行したスレッドは、後で処理の待ち合わせをするために、リストに保存しておく。

キューにタスクを登録する

[cc lang=”python”]
for item in range(100):
q.put(item)
[/cc]

このサンプルではシンプルな数値を設定しているが、キューにはなんでも登録できるので、
DBから取得したデータや、DTOなど、なんでも自由に設定できる。

キューの終端を登録する

[cc lang=”python”]
for t in threads:
q.put(None)
[/cc]

マルチスレッドで実行される関数は、キューからNoneを取得した時点でスレッドを終了するように定義している。
なので、キューに実行すべきキューを登録した後に、スレッドの数だけ終端の合図(None)を設定しておく。

各スレッドが終了することを確認する

[cc lang=”python”]
for t in threads:
t.join()
[/cc]

スレッドのjoin()は、スレッドが完了したときに戻ってくる。
これを利用して、すべてのスレッドのjoin()が完了するまで処理を待つ。

まとめ

これらの応用することで、簡単にマルチスレッドで処理することができる。
私の場合は、処理は他のシステムのAPIを呼ぶことが多い。
他のシステムAPIがネックで、待ちが多く発生しているときなどは、その部分をマルチスレッドにし、
幾つか多重でリクエストをかけている。
同じデータを複数処理してももったいないので、キューに一度に一気に溜め込んでいる。
また、結果出力はsqliteを使うことが多いが、sqlite3はマルチに書き込むと思わぬエラーが発生することがあるので、
sqliteへの書き込みもスレッド化(シングル)し、各マルチ処理は書き込みキューへキューイングすることで実装している。

javaで作ればいくつもクラスを作って冗長になりがちな実装が、pythonだと1ファイルで簡単に作成できてしまう。

同じタグの記事
同じカテゴリの記事

コメント

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA