pythonマルチスレッド同期メカニズム

6941 ワード

ボタンに「1114.順番に印刷」とブラシをかけ、次のように説明します.
難易度94コレクション共有を英語の注目に切り替える
通過回数
14,189
コミット回数
24,486
クラスを提供します.
public class Foo {   public void one() { print("one"); }   public void two() { print("two"); }   public void three() { print("three"); } } 3つの異なるスレッドは、1つのFooインスタンスを共有します.
スレッドAがone()メソッドスレッドBを呼び出すtwo()メソッドスレッドCがthree()メソッドを呼び出すthree()メソッドを呼び出すtwo()メソッドの後にtwo()メソッドが実行され、three()メソッドの後にthree()メソッドが実行されるように修正プログラムを設計してください.
 
例1:
入力:[1,2,3]出力:[onetwothree]解釈:3つのスレッドが非同期で起動されます.[1,2,3]を入力すると、スレッドAがone()メソッドを呼び出し、スレッドBがtwo()メソッドを呼び出し、スレッドCがthree()メソッドを呼び出すことを示します.正しい出力は「onetwothree」です.例2:
入力:[1,3,2]出力:[onetwothree]解釈:入力[1,3,2]はスレッドAがone()メソッドを呼び出し、スレッドBがthree()メソッドを呼び出し、スレッドCがtwo()メソッドを呼び出すことを示す.正しい出力は「onetwothree」です.
以上の説明から分かるように,この問題はバリアを実行する問題であり,スレッド同期方式で解決することができるが,この問題に遭遇した以上,マルチスレッド同期メカニズム:Lock,Semaphores,Condition,Events,queueなどの概念を復習する.
queue:
Queueモジュールはhttps://blog.csdn.net/tryhardsilently/article/details/102684750にまとめられていますが、ここではあまり書きません.次に、本題用queueの実装について説明します.
import queue
class Foo:
    def __init__(self):
        self.tag1 = queue.Queue()
        self.tag2 = queue.Queue()

    def first(self, printFirst: 'Callable[[], None]') -> None:
        # printFirst() outputs "first". Do not change or remove this line.
        printFirst()
        self.tag1.put(1)
        

    def second(self, printSecond: 'Callable[[], None]') -> None:
        self.tag1.get()
        # printSecond() outputs "second". Do not change or remove this line.
        printSecond()
        self.tag2.put(2)


    def third(self, printThird: 'Callable[[], None]') -> None:
        self.tag2.get()
        # printThird() outputs "third". Do not change or remove this line.
        printThird()

Lock:
構築方法:Lock()インスタンス方法:acquire([timeout]):ロックを取得し、スレッドを同期ブロック状態release()にする:ロックを解放します.スレッドが解放される前にロックを取得する必要があります.そうしないと、例外が放出されます.
ロックは利用可能な最下位レベルの同期命令であり,ロック状態の場合,特定のスレッドに所有されない.ロックには、ロックと非ロックの2つの状態と、2つの基本的な方法が含まれています.ロックと解除のプロセスは、ロックの要求-ロックプールに入って待機-ロックの取得-ロック済み-ロックの解除
本題はロックで実現する:
import threading
class Foo:
    def __init__(self):
        self.l1 = threading.Lock()
        self.l2 = threading.Lock()
        self.l1.acquire()
        self.l2.acquire()

    def first(self, printFirst: 'Callable[[], None]') -> None:
        # printFirst() outputs "first". Do not change or remove this line.
        printFirst()
        self.l1.release()
        

    def second(self, printSecond: 'Callable[[], None]') -> None:
        self.l1.acquire()
        # printSecond() outputs "second". Do not change or remove this line.
        printSecond()
        self.l1.release()
        self.l2.release()


    def third(self, printThird: 'Callable[[], None]') -> None:
        self.l2.acquire()
        # printThird() outputs "third". Do not change or remove this line.
        printThird()
        self.l2.release()

Semaphores信号量
信号量はより高度なロック機構である.信号量の内部には、ロック対象の内部にロックIDがないカウンタがあり、占有信号量のスレッド数が信号量を超える場合にのみスレッドがブロックされます.これにより、複数のスレッドが同じコード領域に同時にアクセスできるようになります.
Semaphoreは、acquire()を呼び出すたびにカウンタ-1を内蔵する内蔵カウンタを管理します.release()を呼び出すとカウンタ+1が内蔵されます.
カウンタは0より小さくしてはいけません.カウンタが0の場合、acquire()は他のスレッドがrelease()を呼び出すまでスレッドをブロックします.
Semaphores実装本題:
import threading
class Foo:
    def __init__(self):
        self.s1 = threading.Semaphore()
        self.s2 = threading.Semaphore()
        self.s1.acquire()
        self.s2.acquire()

    def first(self, printFirst: 'Callable[[], None]') -> None:
        # printFirst() outputs "first". Do not change or remove this line.
        printFirst()
        self.s1.release()
        

    def second(self, printSecond: 'Callable[[], None]') -> None:
        self.s1.acquire()
        # printSecond() outputs "second". Do not change or remove this line.
        printSecond()
        self.s1.release()
        self.s2.release()


    def third(self, printThird: 'Callable[[], None]') -> None:
        self.s2.acquire()
        # printThird() outputs "third". Do not change or remove this line.
        printThird()
        self.s2.release()

Event
Event内部にはフラグビットが含まれており、初期はfalseであった.set()を使用してtrueに設定できます.あるいはclear()を使用して新しいfalseに設定します.is_を使用できますset()はフラグビットの状態を検査する.もう1つの最も重要な関数はwait(timeout=None)であり、eventの内部フラグビットがtrueまたはtimeoutタイムアウトに設定されるまで、現在のスレッドをブロックするために使用されます.内部フラグビットがtrueの場合、wait()関数は戻りを理解します.
Event実装本題:
import threading
class Foo:
    def __init__(self):
        self.e1 = threading.Event()
        self.e2 = threading.Event()

    def first(self, printFirst: 'Callable[[], None]') -> None:
        # printFirst() outputs "first". Do not change or remove this line.
        printFirst()
        self.e1.set()
        

    def second(self, printSecond: 'Callable[[], None]') -> None:
        self.e1.wait()
        # printSecond() outputs "second". Do not change or remove this line.
        printSecond()
        self.e1.set()
        self.e2.set()


    def third(self, printThird: 'Callable[[], None]') -> None:
        self.e2.wait()
        # printThird() outputs "third". Do not change or remove this line.
        printThird()
        self.e2.set()

ConditionはConditionを高度なロックと理解することができ、ロック、RLockよりも高度な機能を提供し、複雑なスレッド同期問題を制御することができます.threadiong.Conditionは内部でロックオブジェクト(デフォルトはRLock)を維持し、Condigtionオブジェクトを作成するときにロックオブジェクトをパラメータとして読み込むことができます.Conditionはacquire,releaseメソッドも提供しており,その意味はロックされたacquire,releaseメソッドと一致しているが,実際には内部ロックオブジェクトを簡単に呼び出す対応するメソッドにすぎない.Conditionはまた、以下の方法を提供しています(特に、これらの方法はロック(acquire)を占有した後にのみ呼び出されます.そうしないと、RuntimeError異常が報告されます.):
Condition.wait([timeout]):waitメソッドは内部で占有されているロックを解放し、timeoutパラメータが提供されている場合、通知が起動またはタイムアウトされるまでスレッドが停止する.スレッドが起動され、ロックが再占有されると、プログラムは実行され続けます.
Condition.notify():保留中のスレッドが存在する場合、保留中のスレッドを起動します.注意:notify()メソッドでは、使用するロックは解放されません.
Condition.notify_all()  Condition.notifyAll()は、保留中のスレッドが存在する場合、保留中のすべてのスレッドを呼び覚ます.注意:これらの方法では、使用するロックは解放されません.
condition実装
import threading

class Foo:
    def __init__(self):
        self.c = threading.Condition()
        self.t = 0

    def first(self, printFirst: 'Callable[[], None]') -> None:
        self.res(0, printFirst)

    def second(self, printSecond: 'Callable[[], None]') -> None:
        self.res(1, printSecond)

    def third(self, printThird: 'Callable[[], None]') -> None:
        self.res(2, printThird)
        
    def res(self, val: int, func: 'Callable[[], None]') -> None:
        with self.c:
            self.c.wait_for(lambda: val == self.t) #       ,    bool  
            func()
            self.t += 1
            self.c.notify_all()