Python マルチスレッド
マルチスレッドは、複数の異なるプログラムを同時に実行することに似ています。 マルチスレッドには次の利点があります:
スレッドを使用すると、長期プログラムのタスクをバックグラウンドで処理できます。
ユーザー インターフェイスをより魅力的にすることができ、ユーザーがボタンをクリックして特定のイベントの処理をトリガーすると、プログレス バーがポップアップして処理の進行状況を表示できます
プログラムが高速化される可能性があります
スレッドは、ユーザー入力、ファイルの読み取りと書き込み、ネットワークでのデータの送受信などの待機中のタスクの実装でより便利です。この場合、メモリ使用量などの貴重なリソースを解放できます。
スレッドは、実行中のプロセスとは依然として異なります。それぞれの独立したスレッドには、プログラム実行のエントリ ポイント、順次実行シーケンス、およびプログラムの終了ポイントがあります。ただし、スレッドは独立して実行できず、アプリケーション プログラム内に存在する必要があり、アプリケーション プログラムは複数のスレッドの実行制御を提供します。
各スレッドには、スレッドのコンテキストと呼ばれる独自の CPU レジスタのセットがあり、スレッドが最後に実行されたときの CPU レジスタの状態を反映します。
命令ポインタとスタック ポインタ レジスタは、スレッド コンテキストで最も重要な 2 つのレジスタであり、スレッドは常にプロセス コンテキストで実行されます。これらのアドレスは、スレッドを所有するプロセスのアドレス空間内のメモリをマークするために使用されます。
スレッドはプリエンプト (中断) できます。
他のスレッドの実行中に、スレッドを一時的に保留 (スリープとも呼ばれる) することができます。これをスレッド バックオフといいます。
Python スレッドの学習を始めましょう
Python でスレッドを使用するには、スレッド オブジェクトをラップする関数またはクラスの 2 つの方法があります。
機能: スレッドモジュールの start_new_thread() 関数を呼び出して、新しいスレッドを生成します。構文は次のとおりです:
thread.start_new_thread ( function, args[, kwargs] )
パラメータの説明:
function - スレッド関数。
args - スレッド関数に渡されるパラメータ。タプル型である必要があります。
kwargs - オプションのパラメータ。
例:
#!/usr/bin/python # -*- coding: UTF-8 -*- 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()) ) # 创建两个线程 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.exit() を呼び出すこともできます。スレッド関数。スレッドを終了する目的を達成するために SystemExit 例外をスローします。
スレッド モジュール
Python は、スレッドとスレッドという 2 つの標準ライブラリを通じてスレッドのサポートを提供します。 thread は、低レベルのプリミティブなスレッドと単純なロックを提供します。
スレッド モジュールによって提供されるその他のメソッド:
threading.currentThread(): 現在のスレッド変数を返します。
threading.enumerate(): 実行中のスレッドを含むリストを返します。実行中とは、スレッドの開始後から終了までを指します。ただし、起動前と終了後のスレッドは除きます。
threading.activeCount(): 実行中のスレッドの数を返します。これは、len(threading.enumerate()) と同じ結果になります。
使用方法に加えて、スレッド モジュールはスレッドを処理するための Thread クラスも提供します。 Thread クラスは次のメソッドを提供します:
run(): スレッドのアクティビティを表すために使用されるメソッド。
start():スレッドアクティビティを開始します。
join([time]): スレッドが終了するまで待ちます。これにより、スレッドの join() メソッドが異常終了 (通常終了するか、未処理の例外をスロー) が呼び出されるか、またはオプションのタイムアウトが発生するまで、呼び出しスレッドがブロックされます。
isAlive(): スレッドがアクティブかどうかを返します。
getName(): スレッド名を返します。
setName(): スレッド名を設定します。
Threadingモジュールを使用してスレッドを作成しますThreadingモジュールを使用してスレッドを作成し、threading.Threadから直接継承し、__init__メソッドとrunメソッドを書き換えます:
#!/usr/bin/python # -*- coding: UTF-8 -*- import threading import time exitFlag = 0 class myThread (threading.Thread): #继承父类threading.Thread def __init__(self, threadID, name, counter): threading.Thread.__init__(self) self.threadID = threadID self.name = name self.counter = counter def run(self): #把要执行的代码写到run函数里面 线程在创建后会直接运行run函数 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: thread.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
スレッドの同期 複数のスレッドが共同して特定のデータを変更すると、予測できない結果が発生する可能性があります。データの正確性を確保するには、複数のスレッドを同期する必要があります。 単純なスレッド同期は、Thread オブジェクトの Lock と Rlock を使用することで実現できます。両方のオブジェクトには、一度に 1 つのスレッドのみで操作する必要があるデータの場合、操作を Acquire メソッドと Release メソッドに配置できます。メソッド間のリリース。以下の通り: マルチスレッドの利点は、複数のタスクを同時に実行できることです (少なくともそう感じられます)。ただし、スレッドがデータを共有する必要がある場合、データの非同期の問題が発生する可能性があります。 次の状況を考えてみましょう。リスト内のすべての要素が 0 で、スレッド「set」は後ろから前にすべての要素を 1 に変更し、スレッド「print」はリストを前から後ろに読み取って印刷する責任があります。 その後、おそらくスレッド「set」が変更を開始すると、スレッド「print」がリストを出力し、出力は半分が 0 で半分が 1 になります。これがデータの非同期です。この状況を回避するために、ロックの概念が導入されました。 ロックには、ロックとロック解除の 2 つの状態があります。 「set」などのスレッドが共有データにアクセスしたい場合は、まずロックを取得する必要があります。「print」などの別のスレッドがすでにロックを取得している場合は、スレッド「set」を一時停止させます。これは同期ブロックです。スレッド「Print」まで アクセスが完了してロックが解除された後、スレッド「set」を続行させます。 この処理により、リストを印刷する際に全て0か全て1が出力されるようになり、半分0と半分1という恥ずかしい場面はなくなりました。 例:
#!/usr/bin/python # -*- coding: UTF-8 -*- 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 # 获得锁,成功获得锁定后返回True # 可选的timeout参数不填时将一直阻塞直到获得锁定 # 否则超时后将返回False 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"
スレッド優先度キュー (キュー)Python の Queue モジュールは、FIFO (先入れ先出し) キュー Queue、LIFO (後入れ先出し) キュー LifoQueue などの同期のスレッドセーフ キュー クラスを提供します。優先キューPriorityQueue。これらのキューはロック プリミティブを実装しており、マルチスレッドで直接使用できます。キューを使用して、スレッド間の同期を実現できます。 Queue モジュールでよく使用されるメソッド:
Queue.qsize() キューのサイズを返します
Queue.empty() キューが空の場合は True を返し、それ以外の場合は False を返します
Queue.full() キューがいっぱいの場合は戻りますTrue、そうでない場合は False
Queue.full は maxsize size に対応します
Queue.get([block[, timeout]]) でキューを取得し、タイムアウト待ち時間
Queue.get_nowait() は同等ですQueue.get(False)へ
- Queue.put(item)はキューに書き込み、タイムアウト待ち時間
- Queue.put_nowait(item)はQueue.put(item, False)と同等です
- Queue.task_done() ジョブの完了後、Queue.task_done() 関数はタスクが完了したキューにシグナルを送信します
- Queue.join() は実際には、他の操作を実行する前にキューが空になるまで待機することを意味します
#!/usr/bin/python # -*- coding: UTF-8 -*- 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