マルチスレッドプログラミング
実際には、スレッドが作成された後、スレッドは常に状態を維持するわけではなく、その状態は大まかに次のとおりです:
New は
Runnable Ready を作成します。スケジュールを待っています
実行中実行中
ブロックされましたブロックされました。待機ロック、スリープ状態でブロッキングが発生する可能性があります。
デッドデス
スレッドにはさまざまな状態とさまざまなタイプがあります。大きく分けて次のようになります。
メインスレッド
サブスレッド
デーモンスレッド(バックグラウンドスレッド)
フォアグラウンドスレッド
完全な簡単な理解 この後、具体的なコードの使用法を見ていきます。
1. スレッドの作成
Python には、マルチスレッド操作用の 2 つのモジュール (スレッドとスレッド) が用意されています。
前者は相対的に低レベル モジュールは、下位レベルの操作に使用され、一般的なアプリケーション レベルの開発では一般的に使用されません。
#!/usr/bin/env python3 # -*- coding: UTF-8 -*- import time import threading class MyThread(threading.Thread): def run(self): for i in range(5): print('thread {}, @number: {}'.format(self.name, i)) time.sleep(1) def main(): print("Start main threading") # 创建三个线程 threads = [MyThread() for i in range(3)] # 启动三个线程 for t in threads: t.start() print("End Main threading") if __name__ == '__main__': main()
実行結果:
Start main threading thread Thread-1, @number: 0 thread Thread-2, @number: 0 thread Thread-3, @number: 0 End Main threading thread Thread-2, @number: 1 thread Thread-1, @number: 1 thread Thread-3, @number: 1 thread Thread-1, @number: 2 thread Thread-3, @number: 2 thread Thread-2, @number: 2 thread Thread-2, @number: 3 thread Thread-3, @number: 3 thread Thread-1, @number: 3 thread Thread-3, @number: 4 thread Thread-2, @number: 4 thread Thread-1, @number: 4
ここでの異なる環境の出力結果は明らかに異なることに注意してください。
2. スレッドのマージ (join メソッド)
上記の例で出力された結果から、メインスレッドが終了した後も、子スレッドはまだ実行中です。 。したがって、メインスレッドは、子スレッドの実行が終了するまで待ってから終了する必要があります。
現時点では、join メソッドを使用する必要があります。
上記の例では、次のようなコードを追加します。
#!/usr/bin/env python3 # -*- coding: UTF-8 -*- import time import threading class MyThread(threading.Thread): def run(self): for i in range(5): print('thread {}, @number: {}'.format(self.name, i)) time.sleep(1) def main(): print("Start main threading") # 创建三个线程 threads = [MyThread() for i in range(3)] # 启动三个线程 for t in threads: t.start() # 一次让新创建的线程执行 join for t in threads: t.join() print("End Main threading") if __name__ == '__main__': main()
出力された結果から、上記の例で出力された結果と比較して、メイン スレッドが待機中 子スレッドの実行が終了すると終了します。
Start main threading thread Thread-1, @number: 0 thread Thread-2, @number: 0 thread Thread-3, @number: 0 thread Thread-1, @number: 1 thread Thread-3, @number: 1 thread Thread-2, @number: 1 thread Thread-2, @number: 2 thread Thread-1, @number: 2 thread Thread-3, @number: 2 thread Thread-2, @number: 3 thread Thread-1, @number: 3 thread Thread-3, @number: 3 thread Thread-3, @number: 4 thread Thread-2, @number: 4 thread Thread-1, @number: 4 End Main threading
3. スレッドの同期とミューテックス ロック
スレッドの読み込みを使用してデータを取得すると、通常、データが同期しなくなります。もちろん、この時点でリソースをロックできます。つまり、リソースにアクセスするスレッドは、リソースにアクセスする前にロックを取得する必要があります。
スレッド モジュールはロック機能を提供します。
lock = threading.Lock()
スレッドでロックを取得
lock.acquire()
使用が完了したら、必ずロックを解放する必要があります
lock.release()
もちろん、同じリソースに対する複数のリクエストをサポートするために同じスレッドで、Python はリエントラント ロック (RLock) を利用できます。 RLock は内部的にロックとカウンタ変数を保持しており、カウンタは取得回数を記録するため、リソースが複数回必要になる可能性があります。スレッドのすべての取得が解放されるまで、他のスレッドがリソースを取得できます。
それでは、リエントラント ロックを作成するにはどうすればよいでしょうか?これはコードの問題でもあります:
r_lock = threading.RLock()
4. 条件条件変数
実用的なロックではスレッド同期を実現できますが、より複雑な環境では、いくつかの条件を実装する必要があります。ロックの裁判官のために。 Python は Condition オブジェクトを提供します。 Condition オブジェクトは、特定のイベントがトリガーされた後、または特定の条件が満たされた後にデータを処理するために使用できます。Lock オブジェクトの取得メソッドと解放メソッドに加えて、待機メソッドと通知メソッドも提供します。スレッドは最初に条件変数ロックを取得します。条件が不十分な場合はスレッドは待機し、条件が満たされた場合はスレッドが実行され、他のスレッドに通知することもできます。待ち状態にある他のスレッドは、通知を受け取った後に条件を再判断します。
条件変数は、異なるスレッドが次々にロックを取得していると見なすことができ、条件が満たされない場合は、(Lock または RLock) 待機プールに投入されると理解できます。他のスレッドに直接通知し、状況を再判断してください。このプロセスは、複雑な同期問題を解決するために継続的に繰り返されます。
このモデルは、生産者/消費者モデルでよく使用されます。具体的には、オンライン ショッピングの購入者と販売者の次の例を見てください:
#!/usr/bin/env python3 # -*- coding: UTF-8 -*- import threading, time class Consumer(threading.Thread): def __init__(self, cond, name): # 初始化 super(Consumer, self).__init__() self.cond = cond self.name = name def run(self): # 确保先运行Seeker中的方法 time.sleep(1) self.cond.acquire() print(self.name + ': 我这两件商品一起买,可以便宜点吗') self.cond.notify() self.cond.wait() print(self.name + ': 我已经提交订单了,你修改下价格') self.cond.notify() self.cond.wait() print(self.name + ': 收到,我支付成功了') self.cond.notify() self.cond.release() print(self.name + ': 等待收货') class Producer(threading.Thread): def __init__(self, cond, name): super(Producer, self).__init__() self.cond = cond self.name = name def run(self): self.cond.acquire() # 释放对琐的占用,同时线程挂起在这里,直到被 notify 并重新占有琐。 self.cond.wait() print(self.name + ': 可以的,你提交订单吧') self.cond.notify() self.cond.wait() print(self.name + ': 好了,已经修改了') self.cond.notify() self.cond.wait() print(self.name + ': 嗯,收款成功,马上给你发货') self.cond.release() print(self.name + ': 发货商品') cond = threading.Condition() consumer = Consumer(cond, '买家(两点水)') producer = Producer(cond, '卖家(三点水)') consumer.start() producer.start()
出力結果は次のとおりです:
买家(两点水): 我这两件商品一起买,可以便宜点吗 卖家(三点水): 可以的,你提交订单吧 买家(两点水): 我已经提交订单了,你修改下价格 卖家(三点水): 好了,已经修改了 买家(两点水): 收到,我支付成功了 买家(两点水): 等待收货 卖家(三点水): 嗯,收款成功,马上给你发货 卖家(三点水): 发货商品
5. スレッド間通信
プログラム内に複数のスレッドがある場合、必然的にこれらのスレッドは相互に通信する必要があります。では、これらのスレッド間で情報やデータを安全に交換するにはどうすればよいでしょうか?
おそらく、あるスレッドから別のスレッドにデータを送信する最も安全な方法は、キュー ライブラリのキューを使用することです。複数のスレッドで共有される Queue オブジェクトを作成します。このオブジェクトは、put() および get() 操作を使用してキューに要素を追加または削除します。
# -*- coding: UTF-8 -*- from queue import Queue from threading import Thread isRead = True def write(q): # 写数据进程 for value in ['两点水', '三点水', '四点水']: print('写进 Queue 的值为:{0}'.format(value)) q.put(value) def read(q): # 读取数据进程 while isRead: value = q.get(True) print('从 Queue 读取的值为:{0}'.format(value)) if __name__ == '__main__': q = Queue() t1 = Thread(target=write, args=(q,)) t2 = Thread(target=read, args=(q,)) t1.start() t2.start()
出力結果は次のとおりです:
写进 Queue 的值为:两点水 写进 Queue 的值为:三点水 从 Queue 读取的值为:两点水 写进 Queue 的值为:四点水 从 Queue 读取的值为:三点水 从 Queue 读取的值为:四点水
Python は、スレッド間通信用の Event オブジェクトも提供します。これは、スレッドによって設定されるシグナル フラグです。シグナル フラグが true の場合、他のスレッドはシグナルが接触するまで待機します。
Event オブジェクトは単純なスレッド通信機構を実装しており、スレッド間の通信を実現するためのシグナルの設定、シグナルのクリア、待機などを提供します。
シグナルを設定します
Event の set() メソッドを使用して、Event オブジェクト内のシグナル フラグを true に設定します。 Event オブジェクトは、内部信号フラグのステータスを決定するための isSe() メソッドを提供します。イベント オブジェクトの set() メソッドが使用される場合、isSet() メソッドは true を返します。
信号をクリアします
Event オブジェクトの clear() メソッドを使用して信号をクリアしますEvent オブジェクト内のシグナル フラグ、つまり false に設定します。Event のクリア メソッドが使用される場合、isSet() メソッドは false を返します。 Event オブジェクトの wait メソッドは、内部シグナルが true の場合にのみ機能し、その場合にのみ実行され、すぐに返されます。 Event オブジェクトの内部シグナル フラグが false の場合、wait メソッドは true になるまで待機してから戻ります。
例:
# -*- coding: UTF-8 -*- import threading class mThread(threading.Thread): def __init__(self, threadname): threading.Thread.__init__(self, name=threadname) def run(self): # 使用全局Event对象 global event # 判断Event对象内部信号标志 if event.isSet(): event.clear() event.wait() print(self.getName()) else: print(self.getName()) # 设置Event对象内部信号标志 event.set() # 生成Event对象 event = threading.Event() # 设置Event对象内部信号标志 event.set() t1 = [] for i in range(10): t = mThread(str(i)) # 生成线程列表 t1.append(t) for i in t1: # 运行线程 i.start()出力結果は次のとおりです:
1 0 3 2 5 4 7 6 9 8
6. バックグラウンド スレッド
デフォルトでは、メインスレッドは終了します。その後、子スレッドが参加しなくても終了します。メインスレッドが終了した後も、子スレッドは引き続き実行されます。メインスレッドを終了させたい場合は、そのサブスレッドも終了して実行されなくなるため、サブスレッドをバックグラウンド スレッドとして設定する必要があります。 Python には setDeamon メソッドが用意されています。