Pythonマルチスレッドプログラミング:効率的な並行処理テクニック
スレッドとは
複数のスレッドを実行することは、複数の異なるプログラムを同時に実行することに似ていますが、以下の利点があります。
- プロセス内の複数のスレッドはメインスレッドと同じデータ空間を共有するため、別プロセスの場合よりも簡単に情報を共有したり、互いに通信したりできます。
- スレッドは軽量プロセスと呼ばれることもあり、多くのメモリオーバーヘッドを必要としません。プロセスよりも安価です。
スレッドには開始、実行シーケンス、結論があります。自身のコンテキスト内で現在どこを実行しているかを追跡する命令ポインタを持ちます。
- プリエンプト(割り込み)される可能性がある
- 一時的に保留(スリープとも呼ばれる)状態にでき、他のスレッドが実行中の間は待機 - これをyieldingと呼びます
新しいスレッドの開始
別のスレッドを生成するには、threadモジュールで利用可能な以下のメソッドを呼び出す必要があります。
thread.start_new_thread(function, args[, kwargs])
このメソッド呼び出しにより、LinuxとWindowsの両方で高速かつ効率的に新しいスレッドを作成できます。
メソッド呼び出しは即座に返り、子スレッドが開始して渡されたargsリストで関数を呼び出します。関数が戻るとスレッドは終了します。
ここで、argsは引数のタプルです。引数なしで関数を呼び出すには空のタプルを使用します。kwargsはキーワード引数のオプションの辞書です。
例
#!/usr/bin/python
import thread
import time
# スレッド用の関数を定義
def print_time(threadName, delay):
count = 0
while count < 5:
time.sleep(delay)
count += 1
print("%s: %s" % (threadName, time.ctime(time.time())))
# 2つのスレッドを作成
try:
thread.start_new_thread(print_time, ("Thread-1", 2))
thread.start_new_thread(print_time, ("Thread-2", 4))
except:
print("Error: unable to start thread")
while 1:
pass
実行結果:
Thread-1: Thu Jan 22 15:42:17 2009
Thread-1: Thu Jan 22 15:42:19 2009
Thread-2: Thu Jan 22 15:42:19 2009
Thread-1: Thu Jan 22 15:42:21 2009
Thread-2: Thu Jan 22 15:42:23 2009
Thread-1: Thu Jan 22 15:42:23 2009
Thread-1: Thu Jan 22 15:42:25 2009
Thread-2: Thu Jan 22 15:42:27 2009
Thread-2: Thu Jan 22 15:42:31 2009
Thread-2: Thu Jan 22 15:42:35 2009
低レベルスレッドには非常に効果的ですが、threadモジュールは、次のセクションで説明する新しいthreadingモジュールと比較すると非常に限定的です。
threadingモジュール
Python 2.4に含まれる新しいthreadingモジュールは、前節で説明したthreadモジュールよりもはるかに強力でハイレベルなスレッドサポートを提供します。
threadingモジュールはthreadモジュールのすべてのメソッドを公開し、以下の追加メソッドを提供します。
- threading.activeCount() - アクティブなスレッドオブジェクトの数を返す
- threading.currentThread() - 呼び出し元のスレッド制御内のスレッドオブジェクトの数を返す
- threading.enumerate() - 現在アクティブなすべてのスレッドオブジェクトのリストを返す
メソッドに加えて、threadingモジュールにはスレッドを実装するThreadクラスがあります。Threadクラスが提供するメソッドは以下の通りです。
- run() - スレッドのエントリーポイント
- start() - runメソッドを呼び出してスレッドを開始
- join([time]) - スレッドの終了を待機
- isAlive() - スレッドがまだ実行中かどうかをチェック
- getName() - スレッドの名前を返す
- setName() - スレッドの名前を設定
Threadingモジュールを使用したスレッド作成
threadingモジュールを使用して新しいスレッドを実装するには、以下の手順を行います。
Threadクラスの新しいサブクラスを定義__init__(self [,args])メソッドをオーバーライドして追加引数を追加run(self [,args])メソッドをオーバーライドして、スレッド開始時に実行すべき処理を実装
新しいThreadサブクラスを作成したら、そのインスタンスを作成し、start()を呼び出して新しいスレッドを開始します。これによりrun()メソッドが呼び出されます。
例
#!/usr/bin/python
import threading
import time
exitFlag = 0
class myThread(threading.Thread):
def __init__(self, threadID, name, counter):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.counter = counter
def run(self):
print("Starting " + self.name)
print_time(self.name, 5, self.counter)
print("Exiting " + self.name)
def print_time(threadName, counter, delay):
while counter:
if exitFlag:
threadName.exit()
time.sleep(delay)
print("%s: %s" % (threadName, time.ctime(time.time())))
counter -= 1
# 新しいスレッドを作成
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)
# 新しいスレッドを開始
thread1.start()
thread2.start()
print("Exiting Main Thread")
実行結果:
Starting Thread-1
Starting Thread-2
Exiting Main Thread
Thread-1: Thu Mar 21 09:10:03 2013
Thread-1: Thu Mar 21 09:10:04 2013
Thread-2: Thu Mar 21 09:10:04 2013
Thread-1: Thu Mar 21 09:10:05 2013
Thread-1: Thu Mar 21 09:10:06 2013
Thread-2: Thu Mar 21 09:10:06 2013
Thread-1: Thu Mar 21 09:10:07 2013
Exiting Thread-1
Thread-2: Thu Mar 21 09:10:08 2013
Thread-2: Thu Mar 21 09:10:10 2013
Thread-2: Thu Mar 21 09:10:12 2013
Exiting Thread-2
スレッドの同期
Pythonに付属のthreadingモジュールには、スレッドを同期させるためのシンプルで実装しやすいロック機構が含まれています。新しいロックはLock()メソッドを呼び出して作成し、新しいロックを返します。
新しいロックオブジェクトのacquire(blocking)メソッドを使用して、スレッドを同期的に実行させます。オプションのblockingパラメータで、スレッドがロックの取得を待機するかどうかを制御できます。
blockingが0の場合、ロックが取得できなければ即座に0を返し、取得できれば1を返すblockingが1の場合、スレッドはブロックされ、ロックが解放されるまで待機
新しいロックオブジェクトのrelease()メソッドを使用して、不要になったときにロックを解放します。
例
#!/usr/bin/python
import threading
import time
class myThread(threading.Thread):
def __init__(self, threadID, name, counter):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.counter = counter
def run(self):
print("Starting " + self.name)
# スレッドを同期するためにロックを取得
threadLock.acquire()
print_time(self.name, self.counter, 3)
# 次のスレッドのためにロックを解放
threadLock.release()
def print_time(threadName, delay, counter):
while counter:
time.sleep(delay)
print("%s: %s" % (threadName, time.ctime(time.time())))
counter -= 1
threadLock = threading.Lock()
threads = []
# 新しいスレッドを作成
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)
# 新しいスレッドを開始
thread1.start()
thread2.start()
# スレッドをリストに追加
threads.append(thread1)
threads.append(thread2)
# すべてのスレッドの完了を待機
for t in threads:
t.join()
print("Exiting Main Thread")
実行結果:
Starting Thread-1
Starting Thread-2
Thread-1: Thu Mar 21 09:11:28 2013
Thread-1: Thu Mar 21 09:11:29 2013
Thread-1: Thu Mar 21 09:11:30 2013
Thread-2: Thu Mar 21 09:11:32 2013
Thread-2: Thu Mar 21 09:11:34 2013
Thread-2: Thu Mar 21 09:11:36 2013
Exiting Main Thread
マルチスレッド優先度キュー
Queueモジュールを使用すると、特定数のアイテムを保持できる新しいキューオブジェクトを作成できます。キューを制御するためのメソッドは以下の通りです。
- get() - キューからアイテムを削除して返す
- put() - アイテムをキューに追加
- qsize() - 現在キューにあるアイテム数を返す
- empty() - キューが空ならTrue、そうでなければFalse
- full() - キューが満杯ならTrue、そうでなければFalse
例
#!/usr/bin/python
import Queue
import threading
import time
exitFlag = 0
class myThread(threading.Thread):
def __init__(self, threadID, name, q):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.q = q
def run(self):
print("Starting " + self.name)
process_data(self.name, self.q)
print("Exiting " + self.name)
def process_data(threadName, q):
while not exitFlag:
queueLock.acquire()
if not workQueue.empty():
data = q.get()
queueLock.release()
print("%s processing %s" % (threadName, data))
else:
queueLock.release()
time.sleep(1)
threadList = ["Thread-1", "Thread-2", "Thread-3"]
nameList = ["One", "Two", "Three", "Four", "Five"]
queueLock = threading.Lock()
workQueue = Queue.Queue(10)
threads = []
threadID = 1
# 新しいスレッドを作成
for tName in threadList:
thread = myThread(threadID, tName, workQueue)
thread.start()
threads.append(thread)
threadID += 1
# キューにデータを投入
queueLock.acquire()
for word in nameList:
workQueue.put(word)
queueLock.release()
# キューが空になるまで待機
while not workQueue.empty():
pass
# スレッドに終了を通知
exitFlag = 1
# すべてのスレッドの完了を待機
for t in threads:
t.join()
print("Exiting Main Thread")
実行結果:
Starting Thread-1
Starting Thread-2
Starting Thread-3
Thread-1 processing One
Thread-2 processing Two
Thread-3 processing Three
Thread-1 processing Four
Thread-2 processing Five
Exiting Thread-3
Exiting Thread-1
Exiting Thread-2
Exiting Main Thread
Python
- Fanuc G76 ねじ切りサイクル完全ガイド:CNC機械技師のための総合解説
- ねじ立て vs ねじ転造:主な違いを理解する
- Python データ型マスター: 実践ガイド
- Python 演算子完全マスター: 包括的ガイド
- Python whileループ完全マスター: 構文・例・ベストプラクティス
- Python pass文完全マスター: 実践ガイド
- Python 関数引数完全マスター: 位置引数・キーワード引数・デフォルト引数
- Python辞書完全ガイド:作成、操作、高度なテクニック
- Pythonオブジェクト指向プログラミング完全ガイド:クラス、継承、カプセル化、ポリモーフィズム
- Pythonネットワークプログラミング:ソケットと高度なプロトコルをマスター
- Python C拡張プログラミング:高速ネイティブモジュールの構築