Pythonの道(第4の14)スレッド同期ロック、デッドロック、再帰的ロック、セマフォ

 

マルチスレッドのセキュリティスレッドを確保するためにどのようなアプリケーション、およびスレッド、または共有変数やその他の問題へのアクセス間の同期を使用すると、非常に難しい問題ですが、また問題がマルチスレッドを使用して直面し、適切に処理されない場合は、より多くをもたらすでしょう深刻な結果、Pythonでマルチスレッドを使用すると、共有変数の問題への排他的アクセスを確保するため、スレッド間の同期を確保するために使用されるロックRLOCKセマフォイベント条件を提供しました

ロック&RLOCK:共有変数へのマルチスレッドアクセスを確保するために、ミューテックス

セマフォオブジェクト:ロックミューテックス強化されたバージョンは、また、複数のスレッドを持つことができ、ロックは唯一つのスレッドを同時に所有することができます。

イベントオブジェクト:これは、スレッド間の通信の手段であるが、それは別のスレッドへのスレッドが操作の実行が許可信号を送信することができた後に、信号に対応します。

条件オブジェクト:特定のイベントをトリガーするか、処理後の具体的なデータを得ることができます条件

 

まず、スレッド同期ロック(ミューテックス)

コンセプト

  • スレッドの同期、スレッド間の調整は、あるスレッドが特定のデータにアクセスすることを可能にする技術のいくつかの種類によって、他のスレッドでは、データの操作を完了するために、スレッドまでのデータにアクセスすることはできません。

  • ロック(ロック):スレッドがロックを取得するためにしたら、ロックを取得しようとする他のスレッドを待つためにブロックされます。ロック:場所のための競争はサポートが唯一のユーザーは完全にリソースを使用できることを保証するためにロックを使用することができ、共有がある場合。

 

ロックアプリケーションシナリオ

ロックがアクセスするのに適しており、同じ共有リソースを変更すると、そのリソースは、同じ時間を読み書きします。

ロックノートを使用します。

1、より少ないロック、ロック、必要に応じて有します。複数のスレッドがリソースにアクセスするときにロックがロックされて使用し、それはシリアルまたは実行のためにキューに入れなり、いずれかの実行のために戦って。例えば、高速道路上の車は、並列に実行しているが、唯一のトールゲートを開いている地方の境界に、この開口した後、車両はまだマルチレーン上で一緒に実行することができます。あまりにも車ラインアップに加え、アンロック全く同じ効率場合は、料金所を通過するが、競争と、車両があまりにもロックする必要があります。場合には かかわらず、増加の車両限り、ロックされていないことに注意し、効率が低下しました。

2は、よりよい短いをロックすると、すぐにロックを解除する必要はありません。

3、デッドロックを回避するようにしてください。

 

スレッドロックとGIL(グローバルインタプリタロック)

注意ポイント:1.グラブスレッドはGILロックされ、権限を実行するためにGIL相当をロックするには、ミューテックスロックロック後に実行権限を取得するようになった、他のスレッドはGILをつかむことができますが、ロックを見つけた場合は、まだリリースされていません障害物があってもGILを実行するために許可を得るためにすぐに支払わなければなりません

2.joinは、ミューテックスで参加し、基本的な原理は、同時シリアルになるようにすることですされたデータのセキュリティを確保するために、それはシリアルの一部であり、されているすべてのそのシリアル全体を待っているが、唯一のいくつかの共有データを変更ロックするロックされますそれは、高効率化への疑い、シリアルミューテックスの一部を達成することができます

 

そこにもあるロックする必要がなぜPythonは、1つのスレッドしか実行することを確実にするGIL同じ時間を持っていますか?

 まず、我々は合意に達する必要があります:ロックの目的は、共有データを修正するために一つだけのスレッドが同時に共有データを、保護することです、

   異なるデータ保護が異なるロックに追加する必要があります。そこで、我々は、と結論付けることができます。

 、GILとロックが2つのロック、データの保護が保護されており、(もちろん、そのようなデータのガベージコレクションとしてインタプリタレベルのデータを、保護することです)、元通訳レベル、同じではありませんが、問題となっている。最後に、それは明らかですユーザ独自のアプリケーション開発のデータ、それはGILは、この問題の責任ではないことは明らかである、唯一のユーザー定義のロックロックを扱うことができます。

 

プロセス分析:すべてのスレッドが権限を実行しているGILはロックすべてのスレッドをつかむ、またはつかみます

  その後、スレッド1グラブGILロック、実行権限を取得し、実行、および一握りのロックを追加し、まだ完成し、そのスレッド1がロックを解除されていないていない、スレッド2はGILロックをつかむ可能性があり、実行のプロセスを開始ロックは、スレッド2は、実行ブロックされたアクセス権が奪われているに入り、スレッド1を解放することが見出されていなかった、スレッド1 GILを取得して、ロックを解除する通常行うことが可能となります。これは、シリアル動作の効果につながります

  それはシリアルですので、我々は実行します

  t1.start()

  t1.join

  t2.start()

  t2.join()

  これは、ああ、なぜt1が実行されたコード、すべてのコードロックT1の同等のすべてを待っている参加知り、それをロックする追加シリアル実行され、かつロックコードは、共有データをロックする操作の一部でしかありません。

 

根本的な原因

Pythonインタプリタは自動的に定期的にメモリを回復するためにあなたを支援するので、あなたはPythonインタプリタは、別のスレッドを持って理解することができ、たまにでは、データをクリアすることができ、メモリを参照するグローバル世論調査から目覚めましたあなた自身のスレッドのプログラムとPYインタプリタで独自のスレッドが同時に実行されているこの時点で、あなたは変数のスレッド、この変数を空にする過程で瞬間をクリアスレッドガベージコレクションPYインタプリタを削除した場合、それはかもしれちょうど新しいデータを割り当てることが可能であるメモリ空間をクリアするために再びまだ来ていないし、その結果を割り当てられた他のスレッドは、いつつまり、ロック、Pythonインタプリタは単純で、粗、同様の問題を解決するために、削除されていますスレッドの実行中は、他の人が、これは上記の問題を解決するだろう、動かない、それは以前のバージョンのPythonの遺産とも言えます。

 

前のロック

インポート時間
インポートロックをスレッドからスレッド
#ロック互斥锁加锁前
DEF FUNC(ロック):
    n個のグローバル
    TEMP = N 
    time.sleep(0.2)
    N = TEMP - 1 
__name__ == '__main__'であれば:
    N = 10 
    t_lst = [] 
    ロック=ロック()
    Iの範囲内(10)の場合:
        T =スレッド(目標= FUNC、引数=(ロック))
        t.start()
        t_lst.append(t)を
    tについてでt_lst:t.join()
    プリント(N)#结果很大可能为9

  

分析:複数のスレッドNの10得られた値は、すなわち10-1同時に取得9 N-1の最終値を実行します。

 

ロックした後、

時間インポート
インポートスレッドロックから、スレッド
#ロックミューテックス
DEF FUNC(ロック):
    グローバルN- 
    lock.acquire()
    TEMP = N- 
    time.sleep(0.2)
    。N-TEMP = - 1 
    lock.release()
__name__ IF == '__main__':
    N-10 = 
    t_lst = [] 
    ロックロック=()
    I範囲(10)のための:
        Tはスレッド=(目標= FUNC、引数=(ロック))
        t.start()
        t_lst.append (T)
    t_lstにおけるTのための:t.join()
    プリント(N-)結果が0#で
複数のスレッドが共有データを変更する場合#は、同期制御が必要です。
#スレッドの同期は、リソースの競合、最も簡単な同期機構がミューテックスを導入した複数のスレッドへの安全なアクセスを保証します。
リソース状況の導入のための#のミューテックス:ロック/アンロック。あなたがデータを共有するためのスレッドを変更したい場合は、それは最初にロックされ、
この時間は、リソース#の状態は「ロック」され、他のスレッドを変更することはできません。スレッドまで「非ロック」にリソース、状態のリソースを解放するために
#その他スレッドが再びリソースをロックすることができます。
複数のスレッドのデータの正確さを保証するように、一つだけスレッドが書き込まれることを保証する#ミューテックス。
 

  

第二に、スレッドのデッドロックとロック再帰

デッドロック:いわゆるデッドロックは:実装プロセス内の2つの以上のプロセスまたはスレッドの現象を指し、お互いを待っによって引き起こされるリソースの競合の結果は、外力のない状態で、彼らはそれを促進することができません。この時点で、システムがデッドロック状態やデッドロックと呼ばれる別のプロセスを待っているの過程で常にデッドロックを生成するためのシステムであると言います。1つのスレッドで、私たちは、あなたが同じ共通ロックを使用する場合はデッドロックする傾向があるので、順番にメソッドがロックを使用して、別のメソッドを呼び出し、ロックが解除されていないコードロックlock.acquire()の一部を使用します。

輸入スレッドをスレッドから、ロックの
インポート時
mutexA =ロック()
mutexB =ロック()
クラスMyThread(スレッド):
    デフ(自己)を実行します。
        self.func1()
        self.func2()
    defは関数func1(自己):
        mutexA.acquire ()
        プリント( '\ 033 [41メートル%S拿到A锁\ 033 [0メートル' %のself.name)
        mutexB.acquire()
        プリント( '\ 033 [42メートル%S拿到B锁\ 033 [0メートル' % self.name)
        mutexB.release()
        mutexA.release()
    DEF関数func2(自己):
        mutexB.acquire()
        プリント( '\ 033 [43メートル%S拿到B锁\ 033 [0メートル' %のself.name)
        time.sleep(2) 
        mutexA.acquire()
        印刷( '\ 033 [44メートル% sのロックを取得033 [0メートル\' self.name%)を
        mutexA.release()
        mutexB.release()
__name__ == '__main__' IF:
    (10)の範囲でIについて:
        TはMyThread()= 
        t.start()
「」 ' 
スレッド1がロックを取得する
スレッド1 Bロック取得
スレッド1 Bロック取得
スレッド2ロックが取得
次にスタックデッドロックを
「」 '

  

ソリューション:

threading.RLock:再帰的ロックは、パイソンをサポートするために、同一のリソースに対して同じスレッド複数の要求で、Pythonは「リエントラントロック(再帰的ロック)」を提供します。内部RLOCKは、カウンタは、資源が獲得繰り返しすることができます取得するように回数を記録し、ロックとカウンタ変数を維持します。すべてがリリースされるまで、リソースを取得するために他のスレッドをスレッドを取得します。

 

RLOCKロックの代わりに使用する場合には、表面の例としては、デッドロックが発生しません。

mutexA = mutexB = threading.RLock()#は、このスレッドでは、スレッドロック、カウンタープラス1を取得し、他のすべてのスレッドが唯一のスレッドを待つ、待つことができ、その間にロック場合、カウンタは1を追加し続け、に走りましたカウンタが0になるまで減少されるすべてのロックを解除します

スレッドのインポートをスレッディング、RLOCK、ロック
インポート時間
#mutexA =ロック()
#mutexB =ロック()
mutexA = mutexB = RLOCK()
クラスMyThread(スレッド):
    DEF RUN(セルフ):
        self.func1()
        セルフ.func2()
    DEFのfunc1の(自己):
        mutexA.acquire()カウント#ロックをカウント1 =。
        印刷(self.name%の'\ 033 [%S 41Mは\ 033 [0メートルロックを取得')
        mutexB.acquire( )ロックカウント、カウント#1 +。1 1 = 2 = 
        印刷( '\ 033 [42メートル%S B 033 \ロック取得[0メートル' self.name%)を
        mutexB.release()#ロックカウント数= 2-1 = 1 
        mutexA.release()#1-1ロック= 0カウント=カウント
        mutexB.acquire()#ロックカウント数= 1 
    DEFの関数func2(自己):
        印刷( '\ 033 [43メートルの% sのBが033 [0メートル\ロックされます' self.name%)を
        time.sleep(2)
        mutexA.acquire()ロックカウントカウント#1 +。1 1 = 2 = 
        印刷(「\ 033 [44メートル%S得るロック033 [0メートル「self.name%)を\ 
        mutexA.release()#ロック。1 = 2-1 =カウントカウント
        mutexB.releaseカウント数()#ロック= 1-1 0 = 
__name__ == '__main__' IF:
    範囲内Iのための(10):
        T = MyThread()
        t.start()

  




第三に、信号量セマフォ

ロックなど、内部セマフォオブジェクトは、我々は(内蔵のカウンタ+1)()内蔵のカウンタ-1、呼解放取得を呼び出すたびにダウンカウンタを維持します。カウントは、スレッド0の要求をブロックする方法を取得することが判明した場合、リリースセマフォ後まで、他のスレッドは、カウントが0より大きい場合、回復はスレッドをブロックされています。

セマフォ、からスレッドインポートを通し
インポート時間
DEF FUNC(SEM、I):
    sem.acquire()
    プリント(フォーマット(sem._value) "セマフォ= {} _value取得し、一度に実行される"。) プリントI)
    time.sleep(1)。
    印刷( "セマフォを解放する")
    sem.release()
SEM =セマフォ(5)。
レンジでI(20です)のために:
    スレッド(ターゲット= FUNC、引数の=(SEM、I ))。()開始

  



例2

インポート、タイムスレッド
(threading.Threadの)クラスをmyThread:
    DEF(自己)を実行します、実行メソッドを起動した後#
        semaphore.acquire場合():#ロック5に対応する(複数置くべきロックを追加します、5つのキーを同時に5件のスレッドがあります)
            印刷(self.name)
            time.sleep(5)
            semaphore.release()
IF __name__ == "__main__":
    セマフォ= threading.Semaphore(5)同時に#を持っています(5が最初の5つのスレッドに行くように設定されている)、いくつかの車で駐車場に類似が駐車することができますにいくつかのスレッド
    THRS = []#空のリストを
    範囲(100)に私のために: #100 スレッドが
        (thrs.append myThread())#プラススレッドオブジェクト
    THRSでtについて:
        t.start()#は、それぞれ開始

  

 

 

出力

1スレッド
スレッド2 
スレッド3- 
スレッド4- 
外スレッド#5を5スレッド
のスレッド。8- 
スレッド6- 
スレッド。9- 
スレッド。7- 
スレッド10 3秒ごとに5位、次に印刷
一部省略.......
 

  

 プールプロセスは新しい、セマフォ、わずか4を最初から最後までのプロセスプールプール(4)は、最大4つのだけプロセスを生成することができ、完全に異なる概念ではなく、プロセス/スレッドの束を生成することですプロセス

 

参考資料

[1] https://blog.csdn.net/u013008795/article/details/91357383

[2] https://www.cnblogs.com/nuomin/p/7899675.html

 

おすすめ

転載: www.cnblogs.com/Nicholas0707/p/11440491.html