記事ディレクトリ
序文
前の記事スレッド モジュール ポータルの紹介:
スレッド同期はマルチスレッドにおける重要な概念であり、複数のスレッドがデータを共有する必要がある場合、スレッド同期を使用しないとデータが同期しなくなります。
スレッド同期を実現するには、スレッド ロックと条件変数 Condition の 2 つの方法があります。
1.ねじロック
1.ロックロック
threading モジュールの Lock ロックは、_thread モジュールのロックと同じです。
例:
import threading
import time
num = 0
lock = threading.Lock() #申请线程锁
class threadTest(threading.Thread): #类必须继承threading.Thread
def __init__(self) -> None: #args为传入线程的参数,可根据自己的需求进行定义
super(threadTest,self).__init__() #初始化super()内的必须与类名一样
def run(self) -> None: #定义run()方法,主要写线程的执行内容
global num #声明全局变量num
# lock.acquire() #申请线程锁
print('子线程' + self.getName() + '开始:' + str(time.time()))
while num < 5:
time.sleep(2)
print(self.getName(),'num:',num)
num += 1
#休眠2s
print('子线程' + self.getName() + '结束:' + str(time.time()))
# lock.release() #释放线程锁
return super().run()
if __name__ == '__main__':
print('主线程开始:%s'%(str(time.time())))
thread1 = threadTest()
thread1.setName('Thread-1') #设置线程名称
thread2 = threadTest()
thread2.setName('Thread-2') #设置线程名称
thread1.start() #启动线程
thread2.start() #启动线程
time.sleep(1)
thread1.join()
thread2.join()
print('主线程已结束:%s'%(str(time.time())))
操作結果:
主线程开始:1652803641.5234373
子线程Thread-1开始:1652803641.5236645
子线程Thread-2开始:1652803641.5238614
Thread-2 num: 0
Thread-1 num: 0
Thread-2 num: 2
Thread-1 num: 3
Thread-2 num: 4
子线程Thread-2结束:1652803647.5305896
Thread-1 num: 5
子线程Thread-1结束:1652803647.5308123
主线程已结束:1652803647.531019
ロックスレッドが使用されていない場合、スレッド 1 とスレッド 2 は num 操作について混乱していることがわかります。
上記の 2 行のコード lock.acquire() lock.release() からコメントを削除し、スレッド ロックを追加します。
実行結果は次のとおりです。 上記のような混乱が発生しないことがわかります。
主线程开始:1652804037.7551372
子线程Thread-1开始:1652804037.7553797
Thread-1 num: 0
Thread-1 num: 1
Thread-1 num: 2
Thread-1 num: 3
Thread-1 num: 4
子线程Thread-1结束:1652804047.7664511
子线程Thread-2开始:1652804047.766612
子线程Thread-2结束:1652804047.7667737
主线程已结束:1652804047.7669005
2.Rロックロック
再帰ロックとも呼ばれる RLock ロックは、Lock ロックとは異なり、Lock ロックは同じスレッドで 1 つのアプリケーションしか許可しません。それ以外の場合、スレッドはデッドロックに入りますが、RLock は同じスレッドで複数の呼び出しを許可します。
Lock lock を使用したデッドロックのコード例:
import threading
import time
print('主线程开始:%s'%(str(time.time())))
lock = threading.Lock()
lock.acquire() #申请线程锁
print(threading.enumerate())
lock.acquire() #再次申请线程锁,产生了死锁
print(threading.enumerate())
lock.release()
lock.release()
print('主线程结束:%s'%(str(time.time())))
操作結果:
主线程开始:1652804393.5089679
[<_MainThread(MainThread, started 140384603543360)>]
^Z #此处为我在终端中主动杀死了线程,并非程序自己结束
[1]+ 已停止
RLock ロックを使用しても、デッドロックのサンプル コードは発生しません。
import threading
import time
print('主线程开始:%s'%(str(time.time())))
lock = threading.RLock()
lock.acquire() #申请线程锁
print(threading.enumerate())
lock.acquire() #再次申请线程锁,不会产生死锁
print(threading.enumerate())
lock.release()
lock.release()
print('主线程结束:%s'%(str(time.time())))
操作結果:
主线程开始:1652804609.3881009
[<_MainThread(MainThread, started 140639696934720)>]
[<_MainThread(MainThread, started 140639696934720)>]
主线程结束:1652804609.3881621
上記から、Lock と RLock の違いがわかります。
スレッド ロックはペアで表示する必要があることに注意してください。
2、条件変数 Condition
Condition は python3 のより高度なロックで、スレッド ロックに似た acquire() および release() 関数に加えて、次の関数も提供します。
関数 | 例証する |
---|---|
待って() | スレッドを一時停止します |
通知する() | 中断されたスレッドを起動して実行する |
notifyAll() | すべてのスレッドを起動して実行する |
注: スレッドを使用する前にロックを取得する必要があります。そうしないと、RuntimeError 例外がスローされます。
条件はマルチスレッド通信メカニズムを提供することが理解できます. スレッド 1 がデータを必要とする場合, スレッド 1 はブロックして待機します. スレッド 2 はデータを生成します. スレッド 2 がデータを生成し、スレッド 1 に通知した後, スレッド 1 はデータを取得することができます.
以下は、条件変数 Condition を使用してイディオム ソリティアをシミュレートするためのサンプル コードです。
import threading
import time
class Man1(threading.Thread): #类必须继承threading.Thread
def __init__(self,lock) -> None: #args为传入线程的参数,可根据自己的需求进行定义
super(Man1,self).__init__() #初始化super()内的必须与类名一样
self.lock = lock
def run(self):
self.lock.acquire() #申请锁
print('子线程' + self.getName() + '为所欲为')
self.lock.wait() #挂起线程,等待回答
print('子线程' + self.getName() + '逼上梁山')
self.lock.notify() #运行挂起的线程
self.lock.wait()
print('子线程' + self.getName() + '尽力而为')
self.lock.notify()
self.lock.release()
class Man2(threading.Thread): #类必须继承threading.Thread
def __init__(self,lock) -> None: #args为传入线程的参数,可根据自己的需求进行定义
super(Man2,self).__init__() #初始化super()内的必须与类名一样
self.lock = lock
def run(self):
self.lock.acquire()
self.lock.notify() #唤醒对方线程
print('子线程' + self.getName() + '为法自弊')
self.lock.wait() #挂起线程,等待回答
self.lock.notify()
print('子线程' + self.getName() + '山穷水尽')
self.lock.wait()
self.lock.notify()
print('子线程' + self.getName() + '为所欲为')
self.lock.release()
if __name__ == '__main__':
lock = threading.Condition()
man1 = Man1(lock)
man2 = Man2(lock)
man1.setName('Thread-1') #设置线程名称
man2.setName('Thread-2') #设置线程名称
print('成语接龙开始:')
man1.start()
man2.start()
操作結果:
成语接龙开始:
子线程Thread-1为所欲为
子线程Thread-2为法自弊
子线程Thread-1逼上梁山
子线程Thread-2山穷水尽
子线程Thread-1尽力而为
子线程Thread-2为所欲为
条件変数の制御下で、2 つのスレッドが最後まで順番に実行されることがわかります。