Pythonのマルチプロセスとマルチスレッド


マルチプロセス入門リファレンス Python マップ
Python には、同じ関数に異なるパラメータを常に渡すという問題を特に解決する組み込み関数マップがあります。
参考:
https://zhuanlan.zhihu.com/p/359369130
https://zhuanlan.zhihu.com/p/340657122

1. マルチプロセス

multiprocessing.プロセス
multiprocessing.Pool

1. プロセスプロセスインスタンス

Process クラスのインスタンスを作成し、対象のタスク関数を指定します

# importing the multiprocessing module 
import multiprocessing 

def print_cube(num): 
    print("Cube: {}".format(num * num * num)) 

def print_square(num): 
    print("Square: {}".format(num * num)) 

if __name__ == "__main__": 
    # creating processes 
    p1 = multiprocessing.Process(target=print_square, args=(10, )) 
    p2 = multiprocessing.Process(target=print_cube, args=(10, )) 

    # starting process 1&2
    p1.start() 
    p2.start() 

    # wait until process 1&2 is finished 
    p1.join() 
    p2.join() 

    # both processes finished 
    print("Done!")
def function1(id):  # 这里是子进程
    print(f'id {
      
      id}')

def run__process():  # 这里是主进程
    from multiprocessing import Process
    process = [mp.Process(target=function1, args=(1,)),
               mp.Process(target=function1, args=(2,)), ]
    [p.start() for p in process]  # 开启了两个进程
    [p.join() for p in process]   # 等待两个进程依次结束

# run__process()  # 主线程不建议写在 if外部。由于这里的例子很简单,你强行这么做可能不会报错
if __name__ =='__main__':
    run__process()  # 正确做法:主线程只能写在 if内部

2. プロセスクラス

クラスを定義して Process クラスを継承し、その __init__ メソッドと run メソッドをオーバーライドします。

from multiprocessing import Process
import os
import time


class MyProcess(Process):
    def __init__(self, delay):
        super().__init__()
        self.delay = delay

    # 子进程要执行的代码
    def run(self):
        num = 0
        #for i in range(self.delay * 100000000):
        for i in range(self.delay * 100000):
            num += i
        print(f"进程pid为 {
      
      os.getpid()},执行完成")


if __name__ == "__main__":
    print("父进程pid为 %s." % os.getpid())
    p0 = MyProcess(3)
    p1 = MyProcess(3)
    t0 = time.time()
    print(p0.authkey)
    p0.start()
    p1.start()
    p0.join()
    p1.join()
    t1 = time.time()
    print(f"多进程并发执行耗时 {
      
      t1-t0}")


クラス内でプロセスを作成する

# –*– coding: utf-8 –*–
# @Time      : 2019/3/19 22:58
# @Author    : Damon_duanlei
# @FileName  : process_02.py
# @BlogsAddr : https://blog.csdn.net/Damon_duanlei

import multiprocessing
import time


class A(object):
    def __init__(self):
        self.a = None
        self.b = None
        # 初始化一个共享字典
        self.my_dict = multiprocessing.Manager().dict()

    def get_num_a(self):
        time.sleep(3)
        self.my_dict["a"] = 10

    def get_num_b(self):
        time.sleep(5)
        self.my_dict["b"] = 6

    def sum(self):
        self.a = self.my_dict["a"]
        self.b = self.my_dict["b"]
        print("a的值为:{}".format(self.a))
        print("b的值为:{}".format(self.b))
        ret = self.a + self.b
        return ret

    def run(self,mydata):

        print(mydata)

        p1 = multiprocessing.Process(target=self.get_num_a)
        p2 = multiprocessing.Process(target=self.get_num_b)
        p1.start()
        p2.start()
        p1.join()
        p2.join()
        print(self.sum())


if __name__ == '__main__':

    data={
    
    'name':'jgnhg'}

    t1 = time.time()
    a = A()
    a.run(data)
    t2 = time.time()
    print("cost time :{}".format(t2 - t1))

3. プロセスプールプール

import multiprocessing

# a_list = multiprocessing.Manager().list()
# a_dict = multiprocessing.Manager().dict()

def func(input_list, input_dict, i, k):
    input_list.append(i)
    input_dict[k] = i


if __name__ == '__main__':
    a_list = multiprocessing.Manager().list()
    a_dict = multiprocessing.Manager().dict()

    pool = multiprocessing.Pool(processes=3)
    pool.apply_async(func, (a_list, a_dict, 0, 'a'))
    pool.apply_async(func, (a_list, a_dict, 1, 'b'))
    pool.apply_async(func, (a_list, a_dict, 2, 'c'))
    pool.close()
    pool.join()

    print(a_list)
    print(a_dict)

# 输出
# [0, 2, 1]
# {'a': 0, 'c': 2, 'b': 1}
#coding: utf-8
import multiprocessing
import time

def task(name):
    print(f"{
      
      time.strftime('%H:%M:%S')}: {
      
      name} 开始执行")
    time.sleep(3)

if __name__ == "__main__":
    # 同一时刻有3个进程在执行
    pool = multiprocessing.Pool(processes = 3)
    for i in range(10):
        #维持执行的进程总数为processes,当一个进程执行完毕后会添加新的进程进去
        pool.apply_async(func = task, args=(i,))
    pool.close()
    pool.join()
    print("hello")

# from multiprocessing import Manager,Pool
# import time
#
# def cal(datas,s,e):
#     print(s)
#     sum = 0
#     for i in range(s,e):
#         sum = sum + i
#     datas.append(sum)
#
# if __name__ == '__main__':
#     pool = Pool(2)
#     with Manager() as manager:
#         start_time = time.time()
#
#         datas = manager.list()
#
#         process_list = []
#         for i in range(2):
#             pool.apply_async(func=cal,args=(datas,i*50000000,(1+i)*50000000))
#
#         pool.close()
#         pool.join()
#
#         print('主进程获取datas数据')
#         print(datas[0]+datas[1])
#         print('结束测试')
#         end_time = time.time()
#         print(end_time - start_time)
#         print(datas)
#
# # 1249999975000000
# # 3749999975000000
# # 主进程获取datas数据
# # 4999999950000000
# # 结束测试
# # 1.6817915439605713
# # [1249999975000000, 3749999975000000]

4. プールの 4 つのメソッドの比較:map、apply、map_async、apply_async

プールクラスにはよく使われる以下の4種類があります。

apply:blocking、タスクは実際には 1 つずつ実行されます。並列効果を実現できません
apply_async
マップ
map_async
map と map_async の使用法は似ており、apply と appy_async の使用法も似ています。
非同期との主な違いは次のとおりです。

返される順序は、タスクが作成された順序に基づいていません。すべては、どのタスクが最初に終了し、誰が最初に戻るかによって決まります。
コールバック パラメータがあります。tqdm を組み合わせてマルチプロセスのプログレスバーを作成するなど、戻り値や他のコールバック関数を記録するために使用できます。
非同期関数はパラメータを受け入れますが、同期関数はパラメータを読み込むイテレータを受け入れます

 multiprocessing.dummy Pool()
非阻塞方法
multiprocessing.dummy.Pool.apply_async() 和 multiprocessing.dummy.Pool.imap()
线程并发执行

阻塞方法
multiprocessing.dummy.Pool.apply()和 multiprocessing.dummy.Pool.map()
线程顺序执行

原文链接:https://blog.csdn.net/ye__mo/article/details/123664568

5、参加、デーモン

デフォルトでは、メインプロセスは、すべての子プロセスの実行が完了するまで待ってから終了します。
子プロセスがデーモンプロセスとして設定された後、メインプロセスが完了すると、子プロセスはすぐに終了します
dance_process=multiprocessing.Process(target=dance,args=("lin",3),daemon=True) は
、 join() メソッドをブロックする場合のみ、設定したサブプロセスが終了した後、メインプロセスが開始される
sing_process.start()
dance_process.join()
print("main main process{}".format(os.getpid()) )
(1) プロセス作成後にメインプロセスをブロックする
オブジェクトは後で start() メソッドを通じてプロセスを開始しますが、同時にプロセスがメインプロセスをブロックしたい場合は、join() メソッドを実行できます。プロセスの。通常の状況では、サブプロセスが作成された後、メイン プロセスは最後まで下方向に実行され続けます。サブプロセスがメイン プロセスをブロックした場合、メイン プロセスはサブプロセスの実行が完了するのを待ってから下方向に進みます。 。ここで、メイン プロセスは、終了時刻を計算する前に、サブプロセス p1 と p2 の両方の実行が終了するのを待ちます。

if __name__ == '__main__':
    print('main process is {}'.format(os.getpid()))
    start_time = time.time()
    ### multiprocess
    from multiprocessing import Process
    p1 = Process(target=func)
    p2 = Process(target=func)
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    end_time = time.time()
    print('total time is {}'.format(str(end_time - start_time)))

(2) デーモンプロセス
デーモンプロセス: メインプロセスが終了すると子プロセスも直ちに終了し、生成 された子プロセスは .deamon=True メソッド
使用して子プロセスのプロセスオブジェクトをデーモンプロセスとして設定します。子プロセス内では子プロセスを使用できません デーモンプロセスを作成します デーモンプロセス内で子プロセスをオープンすることはできません、そうしないと例外がスローされます *2.プロセスを開始する前に設定します *3.プロセスはそれぞれ独立してますメインプロセスのコードが終了すると、デーモンプロセスも終了します。



from multiprocessing import Process


def task():
    print('我是子进程!')


if __name__ == '__main__':
    p = Process(target=task)
    # 设置守护进程
    p.daemon = True
    p.start()
    print('我是主进程')


from multiprocessing import Process
import time


# 子进程
def task():
    # 打印123
    print(123)
    # 延时1秒
    time.sleep(1)
    # 打印end123
    print("end123")


if __name__ == '__main__':
    # 生成子进程对象
    p1 = Process(target=task)
    # 开启进程守护
    p1.daemon = True
    # 开启子进行
    p1.start()

    # 延时0.5秒(让子进行执行)
    time.sleep(0.5)
    # 打印main-------
    print("main-------")


6. 共有メモリ(マネージャマルチプロセス変数共有問題)

Value と Array は共有メモリを通じてデータを共有し、
Manager は共有プロセスを通じてデータを共有します。

 	num=multiprocessing.Value('d',1.0)#num=0
    arr=multiprocessing.Array('i',range(10))#arr=range(10)
    p=multiprocessing.Process(target=func1,args=(num,arr))
	manager=Manager()
    list1=manager.list([1,2,3,4,5])
    dict1=manager.dict()
    array1=manager.Array('i',range(10))
    value1=manager.Value('i',1)

コアは、マルチスレッドを処理するときにグローバル変数を使用してデータを共有できます。しかし、これは複数のプロセス間では不可能です。では、複数のプロセス間でデータを共有するにはどうすればよいでしょうか? それなら、共有メモリを使わなければなりません。

multiprocessing.Value を除き、window はマルチプロセスの変数共有宣言をサポートしていません。main 関数以外では、Linux はサポートしています。
num = multiprocessing.Value("d", 1) # d は数値を表します。メインプロセスと子プロセスこの価値観を共有してください。(メインプロセスと子プロセスは同じ値を使用します)
mgr_dict = multiprocessing.Manager().dict({'flag':1,'imgpath':'./static/img','videopath':'./ static/video'}) # メインプロセスは子プロセスとこの辞書を共有します
3.3 Windows でプロセスモジュールを使用する場合の注意事項 Windows
オペレーティングシステムでは、フォーク (Linux オペレーティングシステムでプロセスを作成する機構) が存在しないため、子プロセスを作成するとき、それを開始するファイルが自動的にインポートされ、インポート中にファイル全体が実行されます。したがって、process() をファイルに直接記述すると、サブプロセスが無限に再帰的に作成され、エラーが報告されます。そのため、子プロセスを作成する部分は if name == の判定で保護する必要があり、インポート時に再帰的に実行されません。
https://www.cnblogs.com/SkyOceanchen/p/11537587.html#multiprocessingprocess%E6%A8%A1%E5%9D%97
注: Windows でプロセス モジュールを使用する場合は、name == ' mainの場合、プロセスは現在の .py ファイルに書き込む必要があります' : ステートメントの下では、プロセス モジュールは Windows では通常どおり使用できますが、Unix/Linux では必要ありません。

(1) プロセスのマルチプロセス Process で
定義した複数プロセス間の共有変数は、マルチプロセス下で Value、Array、Queue などを直接使用できますが、リストや辞書を共有したい場合は、強力な Manager モジュールを使用できます。

import multiprocessing


def func(num):
    # 共享数值型变量
    # num.value = 2

    # 共享数组型变量
    num[2] = 9999


if __name__ == '__main__':
    # 共享数值型变量
    # num = multiprocessing.Value('d', 1)
    # print(num.value)

    # 共享数组型变量
    num = multiprocessing.Array('i', [1, 2, 3, 4, 5])
    print(num[:])

    p = multiprocessing.Process(target=func, args=(num,))
    p.start()
    p.join()

    # 共享数值型变量
    # print(num.value)

    # 共享数组型变量
    print(num[:])

(2) プールプロセスプール プロセス
プール間の共有変数は、プロセスプール内のプロセス関係が親子プロセスではないため、上記の方法は使用できません、共有したい場合はManagerモジュールを使用して定義する必要があります。

from multiprocessing import Pool, Manager


def func(my_list, my_dict):
    my_list.append(10)
    my_list.append(11)
    my_dict['a'] = 1
    my_dict['b'] = 2


if __name__ == '__main__':
    manager = Manager()
    my_list = manager.list()
    my_dict = manager.dict()

    pool = Pool(processes=2)
    for i in range(0, 2):
        pool.apply_async(func, (my_list, my_dict))
    pool.close()
    pool.join()

    print(my_list)
    print(my_dict)

(3)所在地

import multiprocessing

# window不支持多进程变量共享声明在main函数外,Linux支持
除multiprocessing.Value外,window不支持多进程变量共享声明在main函数外,Linux支持
num = multiprocessing.Value("d", 0.0)  # d表示数值,主进程与子进程共享这个value。(主进程与子进程都是用的同一个value)
# mgr_list = multiprocessing.Manager().list()  # 主进程与子进程共享这个字典
# mgr_dict = multiprocessing.Manager().dict({'name': 'kunkun'})  # 主进程与子进程共享这个字典

def worker(num, mgr_dict, mgr_list, key, value):
    mgr_dict[key] = value
    mgr_list.append(key)
    num.value += value
    mgr_dict['name'] ='jinmai'
    # print(mgr_dict.get('name'))


if __name__ == '__main__':

    # num = multiprocessing.Value("d", 0.0)  # d表示数值,主进程与子进程共享这个value。(主进程与子进程都是用的同一个value)
    mgr_list = multiprocessing.Manager().list()  # 主进程与子进程共享这个字典
    mgr_dict = multiprocessing.Manager().dict({
    
    'name': 'kunkun'})  # 主进程与子进程共享这个字典

    jobs = [multiprocessing.Process(target=worker, args=(num, mgr_dict, mgr_list, i, i * 2)) for i in range(10)]
    for j in jobs:
        j.start()
    for j in jobs:
        j.join()
    print('Results:')
    print('数字', num.value)
    print('字典', mgr_dict)
    print('列表', mgr_list)
from flask import Flask
import time
import multiprocessing

app = Flask(__name__)
除multiprocessing.Value外,window不支持多进程变量共享声明在main函数外,Linux支持
num = multiprocessing.Value("d", 1)  # d表示数值,主进程与子进程共享这个value。(主进程与子进程都是用的同一个value)

def func(num):

    print('子线程1:', num.value)
    time.sleep(5)
    print('睡眠完成')
    a=1/0
    num.value = 1  # 子进程改变数值的值,主进程跟着改变
    print('子线程2:',num.value)
    # return '子线程处理完成'


@app.route('/')
def hello_world():

    if num.value:
        num.value=0
        p = multiprocessing.Process(target=func, args=(num,))
        p.start()

        print('提交成功,请等待处理')
        return '提交成功,请等待处理'

    if not num.value:
        print('正在执行,请等待')
        return '正在执行,请等待'

    return 'Hello World!'


if __name__ == '__main__':
    app.run(debug=True, port=6006)

ここに画像の説明を挿入します

'''
时间:2021.8.11
作者:手可摘星辰不去高声语
名称:03-进程中执行带有参数的任务.py
'''
 
# 1.导入包和模块
import multiprocessing
import time
 
 
def sing(num, name):
    for i in range(num):
        print(name)
        print("---i am sing ooo~")
        time.sleep(0.5)
 
 
def dance(num, name):
    for i in range(num):
        print(name)
        print("i am dance lll~")
        time.sleep(0.5)
 
 
if __name__ == '__main__':
    # 2.使用进程类创建进程对象
    # target:指定进程执行的函数名,不加括号
    # args:使用元组方式给指定任务传参,顺序一致(参数顺序)
    # kwargs:使用字典的方式给指定任务传参,名称一致(参数名称)
    sing_process = multiprocessing.Process(target=sing, args=(3, "猪猪"))
    dance_process = multiprocessing.Process(target=dance, kwargs={
    
    "name": "珊珊", "num": 2})
 
    # 3. 使用进程对象启动进程执行指定任务
    sing_process.start()
    dance_process.start()

7. プロセスロック

プロセスロックとスレッドロックの使い方は基本的に同じです。プロセス ロックの誕生は、複数のプロセスが共有データをプリエンプトすることを防ぎ、それによって複数のプロセス間で共有メモリが無秩序に変更されることを防ぎます。一度に 1 つのプロセスのみが共有リソースにアクセスできる場合、この状況ではロックを使用する必要があります。
(1) ロック前

# coding:utf-8
import multiprocessing as mp
import time
"""
    进程中的锁lock
"""

def job(v, num):
    for i in range(10):
        v.value += num
        print(v.value)
        time.sleep(0.2)



if __name__ == "__main__":
    # 多进程中的共享内存
    v = mp.Value("i", 0)
    # 进程1让共享变量每次加1
    process1 = mp.Process(target=job, args=(v, 1))
    # 进程2让共享变量每次加3
    process2 = mp.Process(target=job, args=(v, 3))
    process1.start()
    process2.start()


(2)ロック後

# coding:utf-8
import multiprocessing as mp
import time

"""
    进程中的锁lock
"""


def job(v, num, l):
    # 加锁
    l.acquire()
    for i in range(10):
        v.value += num
        print(v.value)
        time.sleep(0.2)
    # 解锁
    l.release()


if __name__ == "__main__":
    # 创建进程锁
    l = mp.Lock()
    # 多进程中的共享内存
    v = mp.Value("i", 0)
    process1 = mp.Process(target=job, args=(v, 1, l))
    process2 = mp.Process(target=job, args=(v, 3, l))
    process1.start()
    process2.start()


multiprocessing模块和threading模块一样也支持锁。通过acquire获取锁,执行操作后通过release释放锁。

#-*- coding:utf8 -*-
from multiprocessing import Process, Lock

def printer(item, lock):
    # 获取锁
    lock.acquire()
    try:
        print(item)
    except Exception as e:
        print(e)
    else:
        print('no exception.')
    finally:
        # 释放锁
        lock.release()

if __name__ == '__main__':
    # 实例化全局锁
    lock = Lock()
    items = ['PHP', 'Python', 'Java']
    procs = []

    for item in items:
        proc = Process(target=printer, args=(item, lock))
        procs.append(proc)
        proc.start()

    for proc in procs:
        proc.join()

    print('Done.')

8. セマフォ (複数のロック)

ミューテックス (スレッド ロック) では、同時に 1 つのスレッドのみがデータを変更できます (1 つのロック)。セマフォ
(Semahpore) では、特定の数のスレッドが同時にデータを変更できます (複数のロック)。
セマフォ: は変数です。パブリック リソースまたはクリティカル セクションへのアクセスを制御します。
セマフォはカウンターを維持し、リソースにアクセスできるスレッドの数、またはクリティカル セクションに同時に入ることができるスレッドの数を指定します。
スレッドがセマフォを取得するたびに、カウンターは -1 されます
。カウンタが 0 の場合、他のスレッドは別のスレッドがセマフォを解放するまで、セマフォへのアクセスを停止します。

from multiprocessing import Process, Semaphore
import time


# 子进程调用的函数
def task(lock, i):
    # 加信号量锁
    lock.acquire()
    print('%s号程序开始执行!' % i)
    time.sleep(2)
    print('%s号程序执行完毕!' % i)
    time.sleep(2)
    # 释放信号量锁
    lock.release()


if __name__ == '__main__':
    # 创建信号量对象, 看成是两把锁
    lock = Semaphore(2)
    # 创建10个子进程
    for i in range(10):
        p = Process(target=task, args=(lock, i))
        p.start()


9. プロセス間通信

プロセス間通信方法には通常、パイプ、メッセージ キュー、シグナル、セマフォ、共有メモリ、ソケットなどが含まれます。ここでは、Python マルチプロセス プログラミングで一般的に使用される multiprocessing.Pipe 関数と multiprocessing.Queue クラスに焦点を当てます。

Pipe
multiprocessing.Pipe() はパイプライン モードで、Pipe() メソッドを呼び出すとパイプの両端の Connection が返されます。Pipe メソッドは、パイプの両端を表す (conn1, conn2) を返します。Pipe メソッドには duplex パラメータがあります。duplex パラメータが True (デフォルト値) の場合、パイプは全二重モードになります。つまり、conn1 と conn2 の両方がメッセージを送受信できます。duplex が False の場合、conn1 はのみですconn2 はメッセージの受信を担当し、conn2 はメッセージの送信のみを担当します。send() メソッドと recv() メソッドは、それぞれメッセージを送信および受信するためのメソッドです。プロセスは、パイプの一端からオブジェクトを入力し、パイプの他端のプロセスによってオブジェクトを受信します。一方向パイプでは、パイプの一端のプロセスにオブジェクトの入力のみが許可され、パイプの一方の端のプロセスがオブジェクトを受信します。逆方向の通信は不可能ですが、双方向パイプでは両端からの入力と出力が可能です。

#-*- coding:utf8 -*-
import os, time
from multiprocessing import Process, Pipe, current_process

def proc1(pipe, data):
    for msg in range(1, 6):
        print('{0} 发送 {1}'.format(current_process().name, msg))
        pipe.send(msg)
        time.sleep(1)
    pipe.close()

def proc2(pipe, length):
    count = 0
    while True:
        count += 1
        if count == length:
            pipe.close()
        try:
            # 如果没有接收到数据recv会一直阻塞,如果管道被关闭,recv方法会抛出EOFError
            msg = pipe.recv()
            print('{0} 接收到 {1}'.format(current_process().name, msg))
        except Exception as e:
            print(e)
            break

if __name__ == '__main__':
    conn1, conn2 = Pipe(True)
    data = range(0, 6)
    length = len(data)
    proc1 = Process(target=proc1, args=(conn1, data))
    proc2 = Process(target=proc2, args=(conn2, length))

    proc1.start()
    proc2.start()

    proc1.join()
    proc2.join()

    print('Done.')

キュー
キューとパイプライン データは両方ともメモリにデータを保存します。
キューはパイプライン ロックに基づいて実装され、キューを使用してデータ共有の問題を解決します。キューの
特性: 先入れ先出し。
共有キューの作成:
キュー オブジェクト = Queue( [maxsize])
キューは複数のプロセス間でデータを安全に転送できます。maxsize
はキュー内で実行されるアイテムの最大数です。省略した場合、制限はありません。

生産および消費モデル: 親プロセス内に 2 つの子プロセスを作成し、1 つはキューにデータを書き込み、もう 1 つはキューからデータを読み取ります。

生产数据的--> 比喻为生产者, 使用数据的--> 比喻为消费者.

生产者处理速度快, 消费者处理速度慢, 则生成者必须等待消费者处理, 有位置存放, 才能继续数据生产.
消费者处理速度快, 生产者处理速度慢, 则消费者必须等待生产者生产, 有了数据后, 才能继续进行处理.

生产者与消费者通过一个容器来解决生产者和消费者的强耦合问题, 
生产者和消费者之间不直接通信, 而是通过阻塞队列进行通信, 
生产者将数据存进阻塞队列中, 消费者从阻塞队列中取数据.

from multiprocessing import Process, Queue
import time


# 生产者
def production(q, name, food):
    for i in range(10):
        date = ('%s生产了第%s个%s.' % (name, i, food))
        date1 = ('%s生产的第%s个%s.' % (name, i, food))
        # 将生产的包子存到队列中
        q.put(date1)
        print(date)


# 消费者
def consumption(q, name):
    for i in range(10):
        time.sleep(1)
        # 从队列中取出包子吃
        print('%s 吃了 %s' % (name, q.get()))


if __name__ == '__main__':
    # 创建队列
    q = Queue(2)

    # 创建生产者子进程
    p1 = Process(target=production, args=(q, 'kid', '包子'))
    # 开始生产
    p1.start()

    # 创建消费者子进程
    p2 = Process(target=consumption, args=(q, 'qz'))
    # 开始消费
    p2.start()




https://zhuanlan.zhihu.com/p/104919288

10. イベント

event.wait() の前でブロックしないでください。event.wait
() までの実行中にプロセスをブロックします。イベントは、プロセスの実行をブロックするために使用されます。ブレークポイントと同様に、プロセスを同時にブロックできます。信号状態を転送するためによく使用されます。イベントはプロセス間の同期通信を実現するために使用されます(もちろんマルチスレッドでもイベントを使用できます)。イベント実行のメカニズムは次のとおりです: Flag はグローバルに定義されます。Flag 値が False の場合、event.wait() メソッドの実行時にプログラムはブロックされます。Flag 値が True の場合、プログラムはイベントの実行時にブロックされません.wait() メソッドは、継続的な実行をブロックします。

Event事件: 事件处理的机制, 通过标志为, 控制全部进程进入阻塞状态,
也可以通过控制标志位,解除全部进程的阻塞.
注意:定义的事件对象, 默认状态是阻塞.


e = Event() #创建一个event对象

e.is_set() #展示当前event的状态,一个对象刚被创建是,状态都为False即阻塞状态

e.wait()  #wait方法会根据当前的对象状态来控制程序是否阻塞

e.set()  #将当前的事件状态改为True

e.clear() #将当前的事件状态改为False

次のコードは 2 つのプロセス関数を定義しています。1 つはイベントの発生を待機するために使用され、もう 1 つはイベントの発生を待機してタイムアウトを設定するために使用されます。メイン プロセスはイベントの set() メソッドを呼び出してウェイクアップします。イベント待ちのプロセスを起動します 起動後、clear()メソッドを使用してイベントのステータスをクリアし、再度待機してプロセスの同期制御を実現します。

import multiprocessing
import time


def wait_for_event(e):
    e.wait()
    time.sleep(1)
    e.clear()
    print(f'{
      
      time.strftime("%H:%M%S")} 进程A 等')
    e.wait()
    print(f'{
      
      time.strftime("%H:%M%S")} 进程A 一起走')


def wait_for_timeout(e, t):
    e.wait()
    time.sleep(1)
    e.clear()
    print(f'{
      
      time.strftime("%H:%M%S")} 进程B 最多等{
      
      t}秒')
    e.wait()
    print(f'{
      
      time.strftime("%H:%M%S")} 进程B 继续走')


if __name__ == '__main__':
    e = multiprocessing.Event()
    w1 = multiprocessing.Process(target=wait_for_event, args=(e,))
    w2 = multiprocessing.Process(target=wait_for_timeout, args=(e, 3))
    w1.start()
    w2.start()
    print(f'{
      
      time.strftime("%H:%M%S")} 主进程 需要5秒')
    e.set()
    time.sleep(8)
    print(f'{
      
      time.strftime("%H:%M%S")} 主进程 赶上')
    e.set()
    w1.join()
    w2.join()
    print(f'{
      
      time.strftime("%H:%M%S")} 主进程 退出')
红绿灯模型
下面这个例子展示的就是event堵塞的效果,当绿灯亮的时候,就会使得cars方法向下执行,而当红灯亮的时候就会堵塞程序的执行

from multiprocessing import Process,Event
import random
from time import sleep

def lights(e):
    while True:
        e.clear()
        print("红灯亮,禁止通行")
        sleep(8)
        e.set()
        print("绿灯亮,冲冲冲")
        sleep(8)

def cars(i,e):
    if e.is_set:
        print("car%i在等待"%i)
        e.wait()
    e.wait()
    print("car%i通过了"%i)

if __name__ == '__main__':
    e = Event()
    p = Process(target=lights,args=(e,))
    p.start()
    car = Process(target=cars,args=(i,e))
    car.start()

11. 複数のプロセスにより +tqdm で進行状況を表示できるようになります

import time
from multiprocessing import Pool, RLock, freeze_support
from tqdm import tqdm
import  os


def my_process(process_name):
    # tqdm中的position参数需要设定呦!!!
    pro_bar = tqdm(range(50), ncols=80, desc=f"Process—{
      
      process_name} pid:{
      
      str(os.getpid())}",
                   delay=0.01, position=process_name, ascii=False)
    for file in pro_bar:
        time.sleep(0.2)
    pro_bar.close()


if __name__ == '__main__':
    print(f'父进程 {
      
      os.getpid()}')
    freeze_support()
    pro_num = 3
    # 多行显示,需要设定tqdm中全局lock
    p = Pool(pro_num, initializer=tqdm.set_lock, initargs=(RLock(),))
    for idx in range(pro_num):
        p.apply_async(my_process, kwds={
    
    "process_name": idx})

    p.close()
    p.join()
# 1、入门https://zhuanlan.zhihu.com/p/163613814

12. プロセスの同期と相互排他

相互排他: 特定のリソースが同時に 1 人の訪問者のみにアクセスを許可し、一意かつ排他的であることを意味します。ただし、相互排除では、訪問者がリソースにアクセスする順序を制限することはできません。つまり、アクセスには順序がありません。

同期: (ほとんどの場合) 相互排他に基づいた他のメカニズムを介した訪問者によるリソースへの秩序あるアクセスを指します。ほとんどの場合、特にリソースへのすべての書き込みが相互排他的である必要がある場合、同期ではすでに相互排他が実装されています。まれに、複数の訪問者が同時にリソースにアクセスできることがあります。

同期は相互排除のより複雑な形式であり、相互排除は特殊な種類の同期です。つまり、相互排他とは、2 つのスレッドが同時に実行できないことを意味します。これらのスレッドは互いに排他します。一方のスレッドの実行が完了するまで、もう一方のスレッドが実行可能になるまで待つ必要があります。同期は同時に実行できませんが、同期する必要があります。対応するスレッドを実行します (これも相互排他です)。

2. マルチスレッド

スレッドの実行シーケンス:
1. サブスレッドの開始 (start()) 後、メインスレッドはサブスレッドを待たず、サブスレッドの実行終了後に自動的に終了します 2. サブスレッドの開始
後、 join() メソッドを呼び出し、メインスレッドはサブスレッドの実行が終了するのを待ちます。
3. 子スレッドを起動する前に setDaemon(True) を設定して、すべての子スレッドをメインスレッドのデーモンスレッドにし、起動後、メインプロセスが終了すると子スレッドも終了します。

1. スレッドのインスタンス化

start() メソッドを呼び出します。

import time
import threading


def task_thread(counter):
    print(
        f'线程名称:{
      
      threading.current_thread().name} 参数:{
      
      counter} 开始时间:{
      
      time.strftime("%Y-%m-%d %H:%M:%S")}'
    )
    num = counter
    while num:
        time.sleep(3)
        num -= 1
    print(
        f'线程名称:{
      
      threading.current_thread().name} 参数:{
      
      counter} 结束时间:{
      
      time.strftime("%Y-%m-%d %H:%M:%S")}'
    )


if __name__ == "__main__":
    print(f'主线程开始时间:{
      
      time.strftime("%Y-%m-%d %H:%M:%S")}')

    # 初始化3个线程,传递不同的参数
    t1 = threading.Thread(target=task_thread, args=(3,))
    t2 = threading.Thread(target=task_thread, args=(2,))
    t3 = threading.Thread(target=task_thread, args=(1,))
    # 开启三个线程
    t1.start()
    t2.start()
    t3.start()
    # 等待运行结束
    t1.join()
    t2.join()
    t3.join()

    print(f'主线程结束时间:{
      
      time.strftime("%Y-%m-%d %H:%M:%S")}')


2. Thread クラスを継承する

サブクラスの run() メソッドと init() メソッドをオーバーライドする

import time
import threading


class MyThread(threading.Thread):
    def __init__(self, counter):
        super().__init__()
        self.counter = counter


    def run(self):

        print(
            f'线程名称:{
      
      threading.current_thread().name} 参数:{
      
      self.counter} 开始时间:{
      
      time.strftime("%Y-%m-%d %H:%M:%S")}'
        )
        counter = self.counter
        while counter:
            time.sleep(3)
            counter -= 1
        print(
            f'线程名称:{
      
      threading.current_thread().name} 参数:{
      
      self.counter} 结束时间:{
      
      time.strftime("%Y-%m-%d %H:%M:%S")}'
        )


if __name__ == "__main__":
    print(f'主线程开始时间:{
      
      time.strftime("%Y-%m-%d %H:%M:%S")}')

    # 初始化3个线程,传递不同的参数
    t1 = MyThread(3)
    t2 = MyThread(2)
    t3 = MyThread(1)
    # 开启三个线程
    t1.start()
    t2.start()
    t3.start()
    # 等待运行结束
    t1.join()
    t2.join()
    t3.join()

    print(f'主线程结束时间:{
      
      time.strftime("%Y-%m-%d %H:%M:%S")}')


import time
import threading


def task_thread(counter):
    print(f'线程名称:{
      
      threading.current_thread().name} 参数:{
      
      counter} 开始时间:{
      
      time.strftime("%Y-%m-%d %H:%M:%S")}')
    num = counter
    while num:
        time.sleep(3)
        num -= 1
    print(f'线程名称:{
      
      threading.current_thread().name} 参数:{
      
      counter} 结束时间:{
      
      time.strftime("%Y-%m-%d %H:%M:%S")}')


class MyThread(threading.Thread):
    def __init__(self, target, args):
        super().__init__()
        self.target = target
        self.args = args

    def run(self):
        self.target(*self.args)


if __name__ == "__main__":
    print(f'主线程开始时间:{
      
      time.strftime("%Y-%m-%d %H:%M:%S")}')

    # 初始化3个线程,传递不同的参数
    t1 = MyThread(target=task_thread,args=(3,))
    t2 = MyThread(target=task_thread,args=(2,))
    t3 = MyThread(target=task_thread,args=(1,))
    # 开启三个线程
    t1.start()
    t2.start()
    t3.start()
    # 等待运行结束
    t1.join()
    t2.join()
    t3.join()

    print(f'主线程结束时间:{
      
      time.strftime("%Y-%m-%d %H:%M:%S")}')


3. スレッドプールプール

マルチスレッドを実装する 4 つの方法は次のとおりです。
(1) マルチプロセッシングには 2 つのタイプがあります。
from multiprocessing.dummy import Pool as ThreadPool # スレッド プール
from multiprocessing.pool import ThreadPool # スレッド プール、使用方法に違いはありません。違いはスレッド プール
(2) 他の 2 つのタイプです:
concurrent.futures から import ThreadPoolExecutor # Python ネイティブ スレッド プール、これはより主流です
import threadpool # スレッド プール、pip install threadpool が必要です、昔の元
のリンク: https:// blog.csdn.net/ye__mo/article/details/123664568

方法 1: multiprocessing.dummy Pool()
ノンブロッキング メソッド: スレッドは
multiprocessing.dummy.Pool.apply_async() と multiprocessing.dummy.Pool.imap() を同時に実行します

ブロッキング方式: スレッドは
multiprocessing.dummy.Pool.apply() と multiprocessing.dummy.Pool.map() を順番に実行します。

from multiprocessing.dummy import Pool as Pool
import time

def func(msg):
    print('msg:', msg)
    time.sleep(2)
    print('end:')
    
pool = Pool(processes=3)
for i in range(1, 5):
    msg = 'hello %d' % (i)
    pool.apply_async(func, (msg,))  # 非阻塞,子线程有返回值
    # pool.apply(func,(msg,))       # 阻塞,apply()源自内建函数,用于间接的调用函数,并且按位置把元祖或字典作为参数传入。子线程无返回值
    # pool.imap(func,[msg,])        # 非阻塞, 注意与apply传的参数的区别 无返回值
    # pool.map(func, [msg, ])       # 阻塞 子线程无返回值

print('Mark~~~~~~~~~~~~~~~')
pool.close()
pool.join()  # 调用join之前,先调用close函数,否则会出错。执行完close后不会有新的进程加入到pool,join函数等待所有子进程结束
print('sub-process done')

方法 3: メインストリームの ThreadPoolExecutor

Python では、スレッド プールを実装するための ThreadPoolExecutor が提供されており、このスレッド プールのデフォルトは子スレッド ガーディアンです。これは、タスクを完了するために多数の突然のリクエストまたは多数のスレッドが必要であるが、実際のタスクの処理時間が短いシナリオに適しています。

from time import sleep
# fun为定义的待运行函数
with ThreadPoolExecutor(max_workers=5) as executor:
    ans = executor.map(fun, [遍历值])
    for res in ans:
        print(res)

with ThreadPoolExecutor(max_workers=5) as executor:
    list = [遍历值]
    ans = [executor.submit(fun, i) for i in list]
    for res in as_completed(ans):
        print(res.result())

このうち、max_workers はスレッド プール内のスレッド数で、一般的に使用されるトラバーサル メソッドには、map や submit+as_completed などがあります。ビジネス シナリオに応じて、出力結果を走査順序で返す必要がある場合は、map メソッドを使用し、最初に完了した人を返したい場合は、submit+as_complete メソッドを使用します。

  • タスクの完了を待機しています
    1. ThreadPoolExecutor がインスタンスを構築するときに、max_workers パラメーターを渡して、スレッド プール内で同時に実行できるスレッドの最大数を設定します。
    2. submit 関数を使用して、スレッドが実行する必要があるタスク (関数名とパラメーター) をスレッド プールに送信し、タスクのハンドルを返します。submit() はブロックせず、すぐに戻ります。
    3. submit 関数によって返されたタスク ハンドルを通じて、done() メソッドを使用してタスクが終了したかどうかを判断できます。
    4. cancel() メソッドを使用して、サブミットされたタスクをキャンセルします。タスクがすでにスレッド プールで実行されている場合、キャンセルすることはできません。
    5. result() メソッドを使用してタスクの戻り値を取得します。内部コードを確認すると、このメソッドがブロックしていることがわかりました。

参考:https://zhuanlan.zhihu.com/p/627853937

当使用 ThreadPoolExecutor 创建的线程池对象后,我们可以使用 submit、map、shutdown等方法来操作线程池中的线程以及任务。

1、submit方法 ThreadPoolExecutor的submit方法用于将任务提交到线程池中进行处理,该方法返回一个Future对象,代表将来会返回结果的值。submit方法的语法如下:

submit(fn, *args, **kwargs)
其中,fn参数是要执行的函数,*args和**kwargs是fn的参数。

示例:

from concurrent.futures import ThreadPoolExecutor

def multiply(x, y):
    return x * y

with ThreadPoolExecutor(max_workers=3) as executor:
    future = executor.submit(multiply, 10, 5)
    print(future.result()) # 50
2map方法 ThreadPoolExecutor的map方法用于将函数应用于迭代器中的每个元素,该方法返回一个迭代器。map方法的语法如下:

map(func, *iterables, timeout=None, chunksize=1)
其中,func参数是要执行的函数,*iterables是一个或多个迭代器,timeout和chunksize是可选参数。

示例:

from concurrent.futures import ThreadPoolExecutor

def square(x):
    return x * x

def cube(x):
    return x * x * x

with ThreadPoolExecutor(max_workers=3) as executor:
    results = executor.map(square, [1, 2, 3, 4, 5])
    for square_result in results:
        print(square_result)

    results = executor.map(cube, [1, 2, 3, 4, 5])
    for cube_result in results:
        print(cube_result)
3、shutdown方法 ThreadPoolExecutor的shutdown方法用于关闭线程池,该方法在所有线程执行完毕后才会关闭线程池。shutdown方法的语法如下:

shutdown(wait=True)
其中,wait参数表示是否等待所有任务执行完毕后才关闭线程池,默认为True。

示例:

from concurrent.futures import ThreadPoolExecutor
import time

def task(num):
    print("Task {} is running".format(num))
    time.sleep(1)
    return "Task {} is complete".format(num)

with ThreadPoolExecutor(max_workers=3) as executor:
    futures = [executor.submit(task, i) for i in range(1, 4)]
    executor.shutdown()

コード例

from concurrent.futures import ThreadPoolExecutor
import threading
import time

# 定义一个准备作为线程任务的函数
def action(max):
    my_sum = 0
    for i in range(max):
        print(threading.current_thread().name + '  ' + str(i))
        my_sum += i
    return my_sum
# 创建一个包含2条线程的线程池
pool = ThreadPoolExecutor(max_workers=2)
# 向线程池提交一个task, 20会作为action()函数的参数
future1 = pool.submit(action, 20)
# 向线程池再提交一个task, 30会作为action()函数的参数
future2 = pool.submit(action, 30)
# 判断future1代表的任务是否结束
print(future1.done())
time.sleep(3)
# 判断future2代表的任务是否结束
print(future2.done())
# 查看future1代表的任务返回的结果
print(future1.result())
# 查看future2代表的任务返回的结果
print(future2.result())
# 关闭线程池
pool.shutdown()


4. スレッドプールの4つの方式の比較 Pool

マルチスレッドを実装する 4 つの方法は次のとおりです。
(1) マルチプロセッシングには 2 つのタイプがあります。
from multiprocessing.dummy import Pool as ThreadPool # スレッド プール
from multiprocessing.pool import ThreadPool # スレッド プール、使用方法に違いはありません。違いはスレッド プール
(2) 他の 2 つのタイプです:
concurrent.futures から import ThreadPoolExecutor # Python ネイティブ スレッド プール、これはより主流です
import threadpool # スレッド プール、pip install threadpool が必要です、昔の元
のリンク: https:// blog.csdn.net/ye__mo/article/details/123664568

5、参加、setDaemon

(1) setDaemon デーモンスレッド
以下の例では、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")
    
----------------------------------

>>> task t1
>>> end

デーモン スレッドをセットアップした後、メイン スレッドが終了すると、サブスレッドもすぐに終了し、実行されなくなることがわかります。

(2) メインスレッドのブロックを結合します。
メインスレッドはサブスレッドの終了を待ちます
。デーモンスレッドの実行終了後にメインスレッドを終了させるには、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")

----------------------------------

>>> task t1
>>> 3
>>> 2
>>> 1
>>> end

6. マルチスレッドでグローバル変数を共有する

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

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()

----------------------------------

>>> in work1 g_num is : 103
>>> in work2 g_num is : 103

7. ネジロック

(1) 同期ロックとミューテックスロック(Lock)
複数のスレッドが同じデータを同時に変更するとダーティデータが発生する可能性があるため、1 つのスレッドが同時に操作できるようにするスレッドロックが発生します。ロックは通常、共有リソースへの同期アクセスを実現するために使用されます。共有リソースごとに Lock オブジェクトを作成します。リソースにアクセスする必要がある場合は、acquire メソッドを呼び出してロック オブジェクトを取得します (他のスレッドがすでにロックを取得している場合は、現在のスレッドは解放されるまで待つ必要があります)、リソースにアクセスした後、release メソッドを呼び出してロックを解放します。スレッドはランダムにスケジュールされるため、複数のスレッドが同時にオブジェクトを操作する場合、オブジェクトが十分に保護されていない場合、プログラムの結果は予測不能になります。これを「スレッド安全でない」とも呼びます。Python の同期ロックは、num 変数を同時に操作するために配置できるスレッドは 1 つだけであることを意味します。

import threading
import time

num = 100

def fun_sub():
    global num
    lock.acquire()
    print('----加锁----')
    print('现在操作共享资源的线程名字是:',t.name)
    num2 = num
    time.sleep(0.001)
    num = num2-1
    lock.release()
    print('----释放锁----')

if __name__ == '__main__':
    print('开始测试同步锁 at %s' % time.ctime())

    lock = threading.Lock() #创建一把同步锁

    thread_list = []
    for thread in range(100):
        t = threading.Thread(target=fun_sub)
        t.start()
        thread_list.append(t)

    for t in thread_list:
        t.join()
    print('num is %d' % num)
    print('结束测试同步锁 at %s' % time.ctime())

(2) デッドロック
Python で複数のリソースをスレッド間で共有している場合、2 つのスレッドがそれぞれリソースの一部を占有し、同時に互いのリソースを待ち続けると、システムがこれらのリソースが使用中であると判断し、デッドロックが発生します。 2 つのスレッドは外部からの力がなければ永遠に待機します。この場合、同じスレッド内で同じリソースが複数回要求されたときに問題が発生します。デッドロックの例を次に示します。

import threading
import time

lock_apple = threading.Lock()
lock_banana = threading.Lock()

class MyThread(threading.Thread):

    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        self.fun1()
        self.fun2()

    def fun1(self):

        lock_apple.acquire()  # 如果锁被占用,则阻塞在这里,等待锁的释放

        print ("线程 %s , 想拿: %s--%s" %(self.name, "苹果",time.ctime()))

        lock_banana.acquire()
        print ("线程 %s , 想拿: %s--%s" %(self.name, "香蕉",time.ctime()))
        lock_banana.release()
        lock_apple.release()


    def fun2(self):

        lock_banana.acquire()
        print ("线程 %s , 想拿: %s--%s" %(self.name, "香蕉",time.ctime()))
        time.sleep(0.1)

        lock_apple.acquire()
        print ("线程 %s , 想拿: %s--%s" %(self.name, "苹果",time.ctime()))
        lock_apple.release()

        lock_banana.release()

if __name__ == "__main__":
    for i in range(0, 10):  #建立10个线程
        my_thread = MyThread()  #类继承法是python多线程的另外一种实现方式
        my_thread.start()

(3) リエントラント ロック (再帰的ロック) RLock
同じスレッド内の同じリソースに対する複数のリクエストをサポートするために、Python は「再帰的ロック」threading.RLock を提供します。RLock は内部で Lock 変数とカウンタ変数を保持しており、カウンタは取得回数を記録するため、リソースを複数回取得できます。スレッドのすべての取得が解放されるまで、他のスレッドはリソースを取得できます。

import threading
import time

lock = threading.RLock()  #递归锁


class MyThread(threading.Thread):

    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        self.fun1()
        self.fun2()

    def fun1(self):

        lock.acquire()  # 如果锁被占用,则阻塞在这里,等待锁的释放

        print ("线程 %s , 想拿: %s--%s" %(self.name, "苹果",time.ctime()))

        lock.acquire()
        print ("线程 %s , 想拿: %s--%s" %(self.name, "香蕉",time.ctime()))
        lock.release()
        lock.release()


    def fun2(self):

        lock.acquire()
        print ("线程 %s , 想拿: %s--%s" %(self.name, "香蕉",time.ctime()))
        time.sleep(0.1)

        lock.acquire()
        print ("线程 %s , 想拿: %s--%s" %(self.name, "苹果",time.ctime()))
        lock.release()

        lock.release()

if __name__ == "__main__":
    for i in range(0, 10):  #建立10个线程
        my_thread = MyThread()  #类继承法是python多线程的另外一种实现方式
        my_thread.start()

(4) with 文のロック Lock
Python Threading の Lock モジュールには、acquire() と release() という 2 つのメソッドがあり、この 2 つのメソッドは with 文との組み合わせに相当します。が最初に実行されます。 の場合、release メソッドはステートメント ブロックが終了した後に実行されます。

举个例子:

from threading import Lock

temp_lock = Lock()

with temp_lock:
   print(temp_lock)
   # 输出是 <locked _thread.lock object at 0x10e304870> 说明temp_lock上锁了

print(temp_lock)
# 输出是<unlocked _thread.lock object at 0x10e304870> 说明temp_lock被释放了
现在来解析一下with语句:

with temp_lock:
    # do something...
相当于以下代码:

temp_lock.acquire()
try:
   # do something...
finally:
   temp_lock.release()

参考:https://zhuanlan.zhihu.com/p/64086821

8. セマフォ (BoundedSemaphore クラス)

ミューテックスでは同時に 1 つのスレッドのみがデータを変更できますが、セマフォでは特定の数のスレッドが同時にデータを変更できます。たとえば、トイレに穴が 3 つある場合、そこに入ることができるのは 3 人だけです。せいぜいトイレくらいで、後ろの人は誰かが出てくるまで待つしかない。

import threading
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-----')

9、通信

スレッドには大きく分けて 3 つの通信メソッドがあります:
threading.Event
threading.Condition
queue.Queue

キューは日常の開発で最も頻繁に使用されます。おそらく、あるスレッドから別のスレッドにデータを送信する最も安全な方法は、キュー ライブラリのキューを使用することです。複数のスレッドで共有される Queue オブジェクトを作成します。このオブジェクトは、put() および get() 操作を使用してキューに要素を送信および取得します。

示例:生产 消费模式

import threading, time
import queue

# 最多存入10个
q = queue.PriorityQueue(10)


def producer(name):
    ''' 生产者 '''
    count = 1
    while True:
        #  生产袜子
        q.put("袜子 %s" % count)  # 将生产的袜子方法队列
        print(name, "---生产了袜子", count)
        count += 1
        time.sleep(0.2)


def consumer(name):
    ''' 消费者 '''
    while True:
        print("%s ***卖掉了[%s]" % (name, q.get()))  # 消费生产的袜子
        time.sleep(1)
        q.task_done()  # 告知这个任务执行完了


# 生产线程
z = threading.Thread(target=producer, args=("张三",))
# 消费线程
l = threading.Thread(target=consumer, args=("李四",))
w = threading.Thread(target=consumer, args=("王五",))

# 执行线程
z.start()
l.start()
w.start()

10. イベント

イベントはスレッド間の通信に使用されます。スレッドがシグナルを送信し、1 つ以上の他のスレッドが待機して Event オブジェクトの wait メソッドを呼び出します。スレッドはブロックされ、他のスレッドが設定されるまで待機してから起動されます。

import threading, time


class Boy(threading.Thread):
    def __init__(self, cond, name):
        super(Boy, self).__init__()
        self.cond = cond
        self.name = name

    def run(self):
        print(self.name + ": 嫁给我吧!?")
        self.cond.set()  # 唤醒一个挂起的线程,让hanmeimei表态
        time.sleep(0.5)
        self.cond.wait()
        print(self.name + ": 我单下跪,送上戒指!")
        self.cond.set()
        time.sleep(0.5)
        self.cond.wait()
        self.cond.clear()
        print(self.name + ": Li太太,你的选择太明智了。")


class Girl(threading.Thread):
    def __init__(self, cond, name):
        super(Girl, self).__init__()
        self.cond = cond
        self.name = name

    def run(self):
        self.cond.wait()  # 等待Lilei求婚
        self.cond.clear()
        print(self.name + ": 没有情调,不够浪漫,不答应")
        self.cond.set()
        time.sleep(0.5)
        self.cond.wait()
        print(self.name + ": 好吧,答应你了")
        self.cond.set()


cond = threading.Event()
boy = Boy(cond, "LiLei")
girl = Girl(cond, "HanMeiMei")
boy.start()
girl.start()


11. マルチスレッド同期の条件

条件オブジェクトを使用すると、スレッド A が停止して他のスレッド B を待つことができます。スレッド B が特定の条件を満たした後、スレッド B に実行を継続するように通知します。
条件 条件変数は通常、ロックに関連付けられます。複数の Condition 条件間でロックを共有する必要がある場合は、Lock/RLock インスタンスをコンストラクターに渡すことができます。それ以外の場合は、RLock インスタンスが独自に生成されます。

import threading


class Boy(threading.Thread):
    def __init__(self, cond, name):
        super(Boy, self).__init__()
        self.cond = cond
        self.name = name

    def run(self):
        self.cond.acquire()
        print(self.name + ": 嫁给我吧!?")
        self.cond.notify()  # 唤醒一个挂起的线程,让hanmeimei表态
        self.cond.wait()  # 释放内部所占用的琐,同时线程被挂起,直至接收到通知被唤醒或超时,等待hanmeimei回答
        print(self.name + ": 我单下跪,送上戒指!")
        self.cond.notify()
        self.cond.wait()
        print(self.name + ": Li太太,你的选择太明智了。")
        self.cond.release()


class Girl(threading.Thread):
    def __init__(self, cond, name):
        super(Girl, self).__init__()
        self.cond = cond
        self.name = name

    def run(self):
        self.cond.acquire()
        self.cond.wait()  # 等待Lilei求婚
        print(self.name + ": 没有情调,不够浪漫,不答应")
        self.cond.notify()
        self.cond.wait()
        print(self.name + ": 好吧,答应你了")
        self.cond.notify()
        self.cond.release()


cond = threading.Condition()
boy = Boy(cond, "LiLei")
girl = Girl(cond, "HanMeiMei")
girl.start()
boy.start()


上記プログラムでは、まずガールスレッドを起動しますが、gitlは条件変数lock condを取得しますが、waitを実行して条件変数lockを解放し、ブロッキング状態になります。ボーイスレッドが開始されると、条件変数 lock cond を取得してメッセージを送信し、notify を通じて中断されたスレッドを起動します。
最後に、リソースはリリース プログラムを通じてリリースされます。

12. GIL (Global Interpreter Lock) グローバル インタープリタ ロック

Python 以外の環境では、シングルコア環境で同時に実行できるタスクは 1 つだけです。マルチコアは、複数のスレッドの同時実行をサポートできます。しかし、Python では、コアがいくつあっても、同時に実行できるスレッドは 1 つだけです。その理由はGILの存在によるものです。

GIL の正式名称は Global Interpreter Lock (グローバル インタープリター ロック) で、そのソースは Python 設計の開始時に考慮され、データ セキュリティのために行われた決定です。スレッドが実行したい場合は、まず GIL を取得する必要があります。GIL は「パス」と考えることができ、Python プロセスには GIL が 1 つだけあります。パスを取得できないスレッドは、実行のために CPU に入ることができません。GIL は cpython でのみ使用できます。cpython は C 言語のネイティブ スレッドを呼び出すため、CPU を直接操作することはできません。同時に 1 つのスレッドのみがデータを取得できるようにするためにのみ GIL を使用できます。pypy と jpython には GIL がありません。https://www.cnblogs.com/luyuze95/p/11289143.html#%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%85%B1%E4%BA%AB%E5 %85%A8%E5%B1%80%E5%8F%98%E9%87%8F

マルチスレッドのリファレンス:
https://blog.csdn.net/cui_yonghua/article/details/119917182
https://www.cnblogs.com/luyuze95/p/11289143.html
https://zhuanlan.zhihu.com/p / 90180209
https://zhuanlan.zhihu.com/p/94909455
https://zhuanlan.zhihu.com/p/64702600
https://blog.csdn.net/ye__mo/article/details/123664568

3. アプリケーション

1. 共有メモリ マルチ
スレッドを処理する場合、コアはグローバル変数を使用してデータを共有できます。しかし、これは複数のプロセス間では不可能です。では、複数のプロセス間でデータを共有するにはどうすればよいでしょうか? それなら、共有メモリを使わなければなりません。

2.
マルチスレッド: Python マルチスレッドは、I/O 集中型のタスクに適しています。I/O 集約型タスクの場合、CPU 計算に費やす時間が減り、ファイルの読み取りと書き込み、Web リクエスト、データベース リクエストなどの I/O に多くの時間が費やされます。マルチプロセス: コンピューティング集約型タスクの
場合タスクでは複数のプロセスを使用する必要があります。

Python はコードの種類によって実行効率も異なります:
1. CPU を大量に使用するコード (さまざまなループ処理、計算など) この場合、多くの計算作業が発生するため、ティック数はすぐにしきい値に達します。そして、GIL のリリースと再競合をトリガーします (複数のスレッド間での切り替えは確実にリソースを消費します)。そのため、Python でのマルチスレッドは CPU を集中的に使用するコードには適していません。
2. IO 集中型のコード (ファイル処理、Web クローラー、およびファイルの読み取りと書き込みを伴うその他の操作) の場合、マルチスレッドは効率を効果的に向上させることができます (単一スレッドで IO 操作がある場合、IO 待機が実行され、不要な処理が発生します)スレッド A の待機中に自動的にスレッド B に切り替えることができ、CPU リソースを無駄にせず、プログラムの実行効率が向上します。したがって、Python のマルチスレッドは IO 集中型のコードに適しています。

比較参照: https://blog.csdn.net/cui_yonghua/article/details/119917182

おすすめ

転載: blog.csdn.net/weixin_44986037/article/details/131417989