Python3-マルチスレッドプログラミング
複数のスレッドを実行することは、いくつかの異なるプログラムを同時に実行することに似ていますが、次の利点があります。
プロセス内の複数のスレッドは、メインスレッドと同じデータスペースを共有するため、別々のプロセスである場合よりも簡単に情報を共有したり、相互に通信したりできます。
スレッドは軽量プロセスと呼ばれることもあり、多くのメモリオーバーヘッドを必要としません。それらはプロセスよりも安価です。
スレッドには、開始、実行シーケンス、および結論があります。コンテキスト内のどこで現在実行されているかを追跡する命令ポインタがあります。
プリエンプト(中断)することができます。
他のスレッドが実行されている間、一時的に保留(スリープとも呼ばれます)にすることができます。これは、yieldingと呼ばれます。
スレッドには2つの異なる種類があります-
- カーネルスレッド
- ユーザースレッド
カーネルスレッドはオペレーティングシステムの一部ですが、ユーザースペーススレッドはカーネルに実装されていません。
Python3でのスレッドの使用をサポートする2つのモジュールがあります-
- _thread
- threading
スレッドモジュールはかなり長い間「非推奨」になっています。代わりにスレッドモジュールを使用することをお勧めします。したがって、Python 3では、モジュール「スレッド」は使用できなくなりました。ただし、Python3での下位互換性のために、名前が「_thread」に変更されました。
新しいスレッドの開始
別のスレッドを生成するには、スレッドモジュールで使用可能な次のメソッドを呼び出す必要があります-
_thread.start_new_thread ( function, args[, kwargs] )
このメソッド呼び出しにより、LinuxとWindowsの両方で新しいスレッドをすばやく効率的に作成できます。
メソッド呼び出しはすぐに戻り、子スレッドが開始され、渡された引数のリストを使用して関数が呼び出されます。関数が戻ると、スレッドは終了します。
ここで、argsは引数のタプルです。空のタプルを使用して、引数を渡さずに関数を呼び出します。kwargsは、キーワード引数のオプションの辞書です。
例
#!/usr/bin/python3
import _thread
import time
# Define a function for the thread
def print_time( threadName, delay):
count = 0
while count < 5:
time.sleep(delay)
count += 1
print ("%s: %s" % ( threadName, time.ctime(time.time()) ))
# Create two threads as follows
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: Fri Feb 19 09:41:39 2016
Thread-2: Fri Feb 19 09:41:41 2016
Thread-1: Fri Feb 19 09:41:41 2016
Thread-1: Fri Feb 19 09:41:43 2016
Thread-2: Fri Feb 19 09:41:45 2016
Thread-1: Fri Feb 19 09:41:45 2016
Thread-1: Fri Feb 19 09:41:47 2016
Thread-2: Fri Feb 19 09:41:49 2016
Thread-2: Fri Feb 19 09:41:53 2016
プログラムは無限ループになります。停止するには、ctrl-cを押す必要があります
低レベルのスレッド化には非常に効果的ですが、スレッドモジュールは新しいスレッド化モジュールと比較して非常に制限されています。
スレッドモジュール
Python 2.4に含まれている新しいスレッドモジュールは、前のセクションで説明したスレッドモジュールよりもはるかに強力で高レベルのスレッドサポートを提供します。
スレッドモジュールは、すべてのメソッド公開さスレッドモジュールを、いくつかの追加の方法を提供します-
threading.activeCount() −アクティブなスレッドオブジェクトの数を返します。
threading.currentThread() −呼び出し元のスレッドコントロール内のスレッドオブジェクトの数を返します。
threading.enumerate() −現在アクティブなすべてのスレッドオブジェクトのリストを返します。
メソッドに加えて、スレッドモジュールにはスレッドを実装するThreadクラスがあります。Threadクラスが提供するメソッドは次のとおりです。
run() − run()メソッドは、スレッドのエントリポイントです。
start() − start()メソッドは、runメソッドを呼び出してスレッドを開始します。
join([time]) − join()は、スレッドが終了するのを待ちます。
isAlive() − isAlive()メソッドは、スレッドがまだ実行中であるかどうかをチェックします。
getName() − getName()メソッドはスレッドの名前を返します。
setName() − setName()メソッドは、スレッドの名前を設定します。
スレッドモジュールを使用したスレッドの作成
スレッドモジュールを使用して新しいスレッドを実装するには、次の手順を実行する必要があります。
Threadクラスの新しいサブクラスを定義します。
__init __(self [、args])メソッドをオーバーライドして、引数を追加します。
次に、run(self [、args])メソッドをオーバーライドして、開始時にスレッドが実行する必要があることを実装します。
新しいThreadサブクラスを作成したら、そのインスタンスを作成し、start()を呼び出して新しいスレッドを開始します。start ()はrun()メソッドを呼び出します。
例
#!/usr/bin/python3
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, self.counter, 5)
print ("Exiting " + self.name)
def print_time(threadName, delay, counter):
while counter:
if exitFlag:
threadName.exit()
time.sleep(delay)
print ("%s: %s" % (threadName, time.ctime(time.time())))
counter -= 1
# Create new threads
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)
# Start new Threads
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print ("Exiting Main Thread")
結果
上記のプログラムを実行すると、次の結果が生成されます-
Starting Thread-1
Starting Thread-2
Thread-1: Fri Feb 19 10:00:21 2016
Thread-2: Fri Feb 19 10:00:22 2016
Thread-1: Fri Feb 19 10:00:22 2016
Thread-1: Fri Feb 19 10:00:23 2016
Thread-2: Fri Feb 19 10:00:24 2016
Thread-1: Fri Feb 19 10:00:24 2016
Thread-1: Fri Feb 19 10:00:25 2016
Exiting Thread-1
Thread-2: Fri Feb 19 10:00:26 2016
Thread-2: Fri Feb 19 10:00:28 2016
Thread-2: Fri Feb 19 10:00:30 2016
Exiting Thread-2
Exiting Main Thread
スレッドの同期
Pythonで提供されるスレッドモジュールには、スレッドを同期できるようにする実装が簡単なロックメカニズムが含まれています。新しいロックは、新しいロックを返すLock()メソッドを呼び出すことによって作成されます。
新しいロックオブジェクトのacquire(blocking)メソッドは、スレッドを強制的に同期的に実行するために使用されます。オプションのブロッキングパラメータを使用すると、スレッドがロックの取得を待機するかどうかを制御できます。
ブロッキングが0に設定されている場合、スレッドは、ロックを取得できない場合は0の値で、ロックを取得した場合は1ですぐに戻ります。ブロッキングが1に設定されている場合、スレッドはブロックし、ロックが解放されるのを待ちます。
新しいロックオブジェクトのrelease()メソッドは、ロックが不要になったときにロックを解放するために使用されます。
例
#!/usr/bin/python3
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)
# Get lock to synchronize threads
threadLock.acquire()
print_time(self.name, self.counter, 3)
# Free lock to release next thread
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 = []
# Create new threads
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)
# Start new Threads
thread1.start()
thread2.start()
# Add threads to thread list
threads.append(thread1)
threads.append(thread2)
# Wait for all threads to complete
for t in threads:
t.join()
print ("Exiting Main Thread")
出力
上記のコードを実行すると、次の結果が得られます。
Starting Thread-1
Starting Thread-2
Thread-1: Fri Feb 19 10:04:14 2016
Thread-1: Fri Feb 19 10:04:15 2016
Thread-1: Fri Feb 19 10:04:16 2016
Thread-2: Fri Feb 19 10:04:18 2016
Thread-2: Fri Feb 19 10:04:20 2016
Thread-2: Fri Feb 19 10:04:22 2016
Exiting Main Thread
マルチスレッド優先キュー
キューのモジュールを使用すると、アイテムの特定の番号を保持することができる新しいキューオブジェクトを作成することができます。キューを制御するには、次の方法があります-
get() − get()は、キューからアイテムを削除して返します。
put() −プットはアイテムをキューに追加します。
qsize() − qsize()は、現在キューにあるアイテムの数を返します。
empty()−キューが空の場合、empty()はTrueを返します。それ以外の場合はFalse。
full()−キューがいっぱいの場合、full()はTrueを返します。それ以外の場合はFalse。
例
#!/usr/bin/python3
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
# Create new threads
for tName in threadList:
thread = myThread(threadID, tName, workQueue)
thread.start()
threads.append(thread)
threadID += 1
# Fill the queue
queueLock.acquire()
for word in nameList:
workQueue.put(word)
queueLock.release()
# Wait for queue to empty
while not workQueue.empty():
pass
# Notify threads it's time to exit
exitFlag = 1
# Wait for all threads to complete
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