Pythonマルチスレッドの説明(マルチスレッド、再帰ロック、ミューテックスロック、セマフォ、イベントなどを実装する方法)

スレッドとは

スレッドは、軽量プロセスとも呼ばれ、オペレーティングシステムが操作のスケジューリングを実行できる最小の単位です。スレッドはプロセスに含まれ、プロセスの実際の操作単位です。スレッド自体はシステムリソースを所有せず、動作中のいくつかの重要なリソースのみを所有しますが、プロセスが所有するすべてのリソースを同じプロセスに属する他のスレッドと共有できます。1つのスレッドで別のスレッドを作成およびキャンセルでき、同じプロセス内の複数のスレッドを同時に実行できます。

マルチスレッドを使用する理由

スレッドは独立しており、プログラム内の同時実行フローです。個別のプロセスと比較すると、プロセス内のスレッド間の分離の程度は小さく、メモリ、ファイルハンドル、および他のプロセスが持つべき状態を共有します。
スレッドの分割スケールはプロセスの分割スケールよりも小さいため、マルチスレッドプログラムの並行性は高くなります。プロセスには実行中に独立したメモリユニットがあり、複数のスレッドがメモリを共有するため、プログラムの効率が大幅に向上します。
スレッドはプロセスよりもパフォーマンスが高くなります。これは、同じプロセス内のスレッドに共通性があるためです。複数のスレッドが同じプロセスの仮想空間を共有します。スレッド共有環境には、プロセスコードセグメント、プロセスパブリックデータなどが含まれます。これらの共有データを使用することで、スレッド間の通信が容易になります。
オペレーティングシステムがプロセスを作成するとき、プロセスに独立したメモリスペースを割り当て、多数の関連リソースを割り当てる必要がありますが、スレッドを作成する方がはるかに簡単です。したがって、並行性を実現するために複数のスレッドを使用するパフォーマンスは、複数のプロセスを使用するよりもはるかに高くなります。
要約すると、マルチスレッドプログラミングの使用には次の利点があります。

  1. プロセス間でメモリを共有することはできませんが、スレッド間でメモリを共有するのは非常に簡単です
  2. オペレーティングシステムがプロセスを作成するとき、プロセスのシステムリソースを再割り当てする必要がありますが、スレッドを作成するコストははるかに小さくなります。したがって、複数のプロセスを使用するよりも、複数のスレッドを使用して複数のタスクの同時実行を実装する方が効率的です。
  3. Python言語には、基盤となるオペレーティングシステムのスケジューリング方法としてだけでなく、マルチスレッド関数のサポートが組み込まれているため、Pythonのマルチスレッドプログラミングが簡素化されます。

スレッドの実装

スレッドモジュールの
通常の作成

import threading

def run(n,i):
    print("我是线程", n,i)


if __name__ == '__main__':
    for i in range(10):
        t1 = threading.Thread(target=run, args=("A",i))
        t2 = threading.Thread(target=run, args=("B",i))
        t1.start()
        t2.start()

パラメータを渡す方法:

argsを使用してパラメーターthreading.Thread(target = fun、args =(
10、100、100 ))を渡しますkwargsを使用してパラメーターthreading.Thread(target = fun、kwargs = {"a":10、 "b":100を渡します、 "c":100})
argsとkwargsの両方を使用して、パラメーターthreading.Thread(target = fun、args =(10、)、kwargs = {"b":100、 "c":100})を渡します。

カスタムスレッド

import threading

class MyThread(threading.Thread):
    def __init__(self, n):
        super(MyThread, self).__init__()  # 重构run函数必须要写
        self.n = n

    def run(self):
        print("我是线程", self.n)


if __name__ == "__main__":
    for i in range(10):
        t1 = MyThread("t1")
        t2 = MyThread("t2")
        t1.start()
        t2.start()

デーモンスレッド
setDaemon(True)を使用して、すべての子スレッドをメインスレッドのデーモンスレッドに変換します。これにより、メインプロセスが終了すると、子スレッドも終了します。したがって、メインスレッドが終了すると、プログラム全体が終了します。

import threading
import time


def run(n):
    print("task", n)
    time.sleep(1)  # 此时子线程停1s
    print('3')
    time.sleep(1)
    print('2')
    time.sleep(1)
    print('1')


if __name__ == '__main__':
    t = threading.Thread(target=run, args=("t1",))
    t.setDaemon(True)  # 把子进程设置为守护线程,必须在start()之前设置
    t.start()
    print("end")

コードでは、
tとprint( "end")は異なるスレッドであり、print( "end")はメインスレッドです。

メインスレッドは子スレッドの終了を待機します。
デーモンスレッドの実行が終了した後にメインスレッドを終了させるために、joinメソッドを使用して、メインスレッドに子スレッドの実行を待機させることができます。

import threading
import time

def run(n):
    print("task", n)
    time.sleep(1)       #此时子线程停1s
    print('3')
    time.sleep(1)
    print('2')
    time.sleep(1)
    print('1')

if __name__ == '__main__':
    t = threading.Thread(target=run, args=("t1",))
    t.setDaemon(True)   #把子进程设置为守护线程,必须在start()之前设置
    t.start()
    t.join() # 设置主线程等待子线程结束
    print("end")

グローバル変数のマルチスレッド共有
スレッドはプロセスの実行単位であり、プロセスはリソースのシステム割り当ての最小単位であるため、同じプロセス内の複数のスレッドがリソースを共有します。

import threading
import time

g_num = 100

def work1():
    global g_num
    for i in range(3):
        g_num += 1
    print("in work1 g_num is : %d" % g_num)

def work2():
    #global g_num
    print("in work2 g_num is : %d" % g_num)

if __name__ == '__main__':
    t1 = threading.Thread(target=work1)
    t1.start()
    time.sleep(1)
    t2 = threading.Thread(target=work2)
    t2.start()

相互排他ロック
はスレッド間でランダムにスケジュールされ、各スレッドはn回の実行しか実行できないため、複数のスレッドが同じデータを同時に変更すると、ダーティデータが表示される可能性があります。したがって、スレッドロックが発生します。つまり、同時に許可されます。操作を実行するための1つのスレッド。スレッドロックはリソースをロックするために使用されます。次のコードのように、複数のロックを定義できます。リソースを独占する必要がある場合、異なるロックを使用して同じものをロックできるのと同じように、どのロックでもリソースをロックできます。ドアがロックされている理由。

スレッドはランダムにスケジュールされるため、複数のスレッドが同時にオブジェクトを操作する場合、オブジェクトが十分に保護されていないと、プログラムの結果は予測できません。これを「スレッドセーフではない」とも呼びます。
上記の事態を防ぐために、相互排他ロック(ロック)が登場しました

ロックを使用しない場合

import time, threading

# 假定这是你的银行卡余额:
balance = 0

def change_it(n):
    # 先存后取,结果应该为0:
    global balance
    balance = balance + n
    balance = balance - n


def run_thread(n):
    for i in range(1000000):
        change_it(n)

t1 = threading.Thread(target=run_thread, args=(5,))
t2 = threading.Thread(target=run_thread, args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
print(balance)

共有変数バランスが定義され、初期値は0で、2つのスレッドが開始され、最初に格納されてから取得されます。理論的には、結果は0になります。ただし、スレッドのスケジューリングはオペレーティングシステムによって決定されるため、t1とt2が交互に実行されます。サイクル数が十分である限り、バランスの結果は必ずしも0ではありません。

その理由は、高水準言語のステートメントは、単純な計算であっても、CPUの実行時に複数のステートメントであるためです。

balance = balance + n

また、2つのステップに分かれています。

x = balance + n
balance = x

2つのステートメントが複数回実行される場合、0ではない可能性があります
。理由は、バランスを変更するために複数のステートメントが必要なためです。これらのステートメントが実行されると、スレッドが中断され、複数のスレッドが同じ内容を変更する可能性があります。オブジェクト。めちゃくちゃ。

2つのスレッドが同時に入出金する場合、残高が間違っている可能性があります。銀行預金が何らかの形でマイナスになることは絶対に避けてください。したがって、一方のスレッドが残高を変更するときに、もう一方のスレッドが変更されないようにする必要があります。それ。
バランス計算が正しいことを確認したい場合は、change_it()にロックを与える必要があります。スレッドがchange_it()の実行を開始すると、スレッドがロックを取得したため、他のスレッドはでchange_it()を実行できないと言います。同時に、ロックが解除されるまで待つことができ、ロックを取得した後で変更することができます。ロックは1つしかないため、スレッドの数に関係なく、同時にロックを保持するスレッドは最大で1つだけであり、変更の競合は発生しません。ロックの作成は、threading.Lock()によって実現されます。

import time, threading

# 假定这是你的银行卡余额:
balance = 0

def change_it(n):
    # 先存后取,结果应该为0:
    global balance
    balance = balance + n
    balance = balance - n


lock = threading.Lock()
def run_thread(n):
    for i in range(1000000):
        # 先要获取锁:
        lock.acquire()
        try:
            # 放心地改吧:
            change_it(n)
        finally:
            # 改完了一定要释放锁:
            lock.release()


t1 = threading.Thread(target=run_thread, args=(5,))
t2 = threading.Thread(target=run_thread, args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
print(balance)

再帰ロック
RLcokクラスの使用法はLockクラスとまったく同じですが、ネストをサポートしており、RLcokクラスは通常、複数のロックが解放されていない場合に使用されます。

import threading
import time

def Func(lock):
    global gl_num
    lock.acquire()
    gl_num += 1
    time.sleep(1)
    print(gl_num)
    lock.release()

if __name__ == '__main__':
    gl_num = 0
    lock = threading.RLock()
    for i in range(10):
        t = threading.Thread(target=Func, args=(lock,))
        t.start()

セマフォ(BoundedSemaphoreクラス)
ミューテックスでは、1つのスレッドのみが同時にデータを変更できますが、セマフォでは、特定の数のスレッドが同時にデータを変更できます。たとえば、トイレに3つのピットがある場合、3人だけです。トイレに行くことが許されており、後ろの人は誰かが出てくるまで待ってから再び入ることができます。

import time

def run(n, semaphore):
    semaphore.acquire()   #加锁
    time.sleep(1)
    print("run the thread:%s\n" % n)
    semaphore.release()     #释放

if __name__ == '__main__':
    num = 0
    semaphore = threading.BoundedSemaphore(5)  # 最多允许5个线程同时运行
    for i in range(22):
        t = threading.Thread(target=run, args=("t-%s" % i, semaphore))
        t.start()
    while threading.active_count() != 1:
        pass  # print threading.active_count()
    else:
        print('-----all threads done-----')

イベント(イベントクラス)

Pythonスレッドのイベントは、メインスレッドが他のスレッドの実行を制御するために使用されます。このイベントは、主に次のメソッドを提供する単純なスレッド同期オブジェクトです。

clearフラグを「False」に
設定しますsetフラグを「True」に
設定しますis_setフラグを設定するかどうかを決定します。Wait
は常にフラグをリッスンします。フラグが検出されない場合は常にブロック状態になります。
イベント処理メカニズム:「フラグ」はグローバルに定義されます。フラグ値が「False」の場合、event.wait()はブロックされ、フラグ値が「True」の場合、event.wait()はブロックされなくなります。

#利用Event类模拟红绿灯
import threading
import time

event = threading.Event()


def lighter():
    count = 0
    event.set()     #初始值为绿灯
    while True:
        if 5 < count <=10 :
            event.clear()  # 红灯,清除标志位
            print("\33[41;1mred light is on...\033[0m")
        elif count > 10:
            event.set()  # 绿灯,设置标志位
            count = 0
        else:
            print("\33[42;1mgreen light is on...\033[0m")

        time.sleep(1)
        count += 1

def car(name):
    while True:
        if event.is_set():      #判断是否设置了标志位
            print("[%s] running..."%name)
            time.sleep(1)
        else:
            print("[%s] sees red light,waiting..."%name)
            event.wait()
            print("[%s] green light is on,start going..."%name)

light = threading.Thread(target=lighter,)
light.start()

car = threading.Thread(target=car,args=("MINI",))
car.start()

ここに画像の説明を挿入します
この記事は主にソース
https://www.cnblogs.com/luyuze95/p/11289143.htmlを参照しています

おすすめ

転載: blog.csdn.net/kobeyu652453/article/details/112778162