ホームページ  >  記事  >  バックエンド開発  >  Python プロセス プールとプロセス ロックの詳細については、こちらをご覧ください。

Python プロセス プールとプロセス ロックの詳細については、こちらをご覧ください。

WBOY
WBOY転載
2022-05-10 18:11:232664ブラウズ

この記事では、python に関する関連知識を提供します。主に、プロセス プール作成モジュールやプロセス プール関数など、プロセス プールとプロセス ロックに関連する問題を紹介します。見てみましょう。皆様のお役に立てれば幸いです。

Python プロセス プールとプロセス ロックの詳細については、こちらをご覧ください。

#推奨される学習:

Python ビデオ チュートリアル

プロセス プール

プロセス プールとは

プロセスに関する問題については前章で述べましたが、プロセスを作成しすぎるとリソースを大量に消費します。この状況を回避するにはプロセス数を修正する必要があり、このときプロセス プールの助けが必要です。

プロセス プールは、あらかじめ一定数のプロセスが作成されているプールと考えることができます。下の図を参照してください。


Python プロセス プールとプロセス ロックの詳細については、こちらをご覧ください。


たとえば、この赤い長方形の配列はプロセス プールを表しており、このプールには 6 つのプロセスがあります。これら 6 つのプロセスはプロセス プールとともに作成されますが、それだけではなく、オブジェクト指向のライフ サイクルを学習するときに、インスタンス化された各オブジェクトは使用後にメモリ マネージャーによってリサイクルされると述べました。

私たちのプロセスは、作成およびシャットダウンのプロセスとともにメモリ マネージャーによってもリサイクルされます。これはそれぞれの場合に当てはまります。プロセスが閉じられるときに作成されるプロセスも、一定量のパフォーマンスを消費します。プロセスプール内のプロセスは作成後にクローズされず、常に再利用できるため、作成とクローズによるリソースの消費が回避され、作成とクローズの繰り返し操作が回避され、効率が向上します。

もちろん、プログラムの実行が終了し、プロセス プールが閉じられると、プロセスも閉じられます。

実行する必要のあるタスクがある場合、現在のプロセス プールにアイドル プロセスがあるかどうかを判断します (いわゆるアイドル プロセスは、実際にはプロセス内でタスクを実行しないプロセスです)プール)。プロセスがアイドル状態の場合、タスクはタスクを実行するプロセスを見つけます。現在のプロセス プール内のすべてのプロセスが非アイドル状態の場合、タスクは待機状態になり、プロセス プール内のプロセスがアイドル状態になってタスクを実行するまで、プロセス プールに出入りできません。

これはプロセス プールの役割です。

#プロセス プールの作成モジュール - マルチプロセス

プロセス プール関数の作成 - プール

##関数名プール
概要 パラメータ 戻り値
プロセス プールの作成 Processcount プロセス プール オブジェクト
プール関数の紹介: "multiprocessing" モジュールの呼び出しによる"Pool" 関数は、"プロセス プール オブジェクト" の作成に役立ちます。この関数には、このプロセス プールに作成するプロセスの数を表すパラメーター "Processcount" (整数) があります。

プロセス プールの一般的に使用されるメソッド

プロセス プール オブジェクトを作成した後、そのプロセスを操作する必要があります。使用されたメソッド。メソッド (関数)。

#関数名概要パラメータ戻り値apply_asyncタスクがプロセス プールに追加されました (非同期)func, argsNoneプロセス プールを閉じるプロセス プールを待ちます終了するタスク
# #close
None None join
なし なし
  • apply_async 関数: この関数はタスクをプロセス プールに追加することであり、非同期で実装されます。 非同期 この知識はまだ学習していないので、それが何を意味するかについては心配しないでください。これには 2 つのパラメータがあります: func と agrs、func はプロセス プールに追加される関数、args は関数のパラメータを表すタプルで、プロセスを作成して使用するときとまったく同じです。 。 一貫性のある。
  • close 関数: プロセス プールの使用が終了したら、close 関数を呼び出してプロセス プールを閉じることができます。パラメータも戻り値もありません。
  • 結合関数: 前の章でプロセスを作成するために学習した結合関数と一致しています。プロセス プール内のすべてのタスクが実行された後でのみ、後続のタスクが実行されます。ただし、これは通常、プロセス プールを閉じるときに使用されます (close 関数 )。

apply_async 関数のデモ ケース

次に、プロセス プールの使用を練習するためのスクリプトを Pycharm で作成します。

  • 関数を定義し、関数が毎回実行される回数とプロセス番号を出力します。
  • プロセス プールの数と実行されるプロセスの数を定義します。毎回プロセスプールに設定する最大プロセス数

サンプルコードは以下のとおりです:

# coding:utf-8import osimport timeimport multiprocessingdef work(count):    
# 定义一个 work 函数,打印输出 每次执行的次数 与 该次数的进程号
    print('\'work\' 函数 第 {} 次执行,进程号为 {}'.format(count, os.getpid()))
    time.sleep(3)
    # print('********')if __name__ == '__main__':
    pool = multiprocessing.Pool(3)      
    # 定义进程池的进程数量,同一时间每次执行最多3个进程
    for i in range(21):
        pool.apply_async(func=work, args=(i,))      
        # 传入的参数是元组,因为我们只有一个 i 参数,所以我们要写成 args=(i,)

    time.sleep(15)      
    # 这里的休眠时间是必须要加上的,否则我们的进程池还未运行,主进程就已经运行结束,对应的进程池也会关闭。
実行結果は次のとおりです:

Python プロセス プールとプロセス ロックの詳細については、こちらをご覧ください。

上の図を見ると、毎回 3 つのプロセスが同時に実行されていることがわかります。各プロセスのプロセス番号は異なりますが、よく見ると次のことがわかります。同じプロセス番号が存在します。これは、プロセス プールが再利用されていることを示します。これは、上で紹介したことを証明しており、プロセス プール内のプロセスは閉じられず、繰り返し使用できます。

また、プロセス プールにはプロセスが 3 つしかないため、3 秒ごとに 3 つのプロセスが実行されていることもわかります。ただし、作業関数 for ループ

には 21 個のタスクがあります。は 21 回実行されますが、プロセス プールにはプロセスが 3 つしかないためです。したがって、3 つのタスクを実行した後 (3 秒間スリープ)、後続のタスクはプロセス プール内のプロセスがアイドル状態になるのを待ってから実行を続行します。

同様に、プロセス番号の順序にも多少の違いがありますが、これは asynchronous 方式 (asynchronous は非同期の意味) を使用しているためです。これにより、work function によって一緒に実行される 3 つのタスクの順序が狂い、プロセス番号が一貫性のない順序で表示されるのはそのためです。 (非同期の知識については、非同期の章で詳しく紹介します

)

プロセス プールの原則: 上記のスクリプト ケースは、プロセスに対するプロセス プールの制限を確認します。プロセス プール内のプロセスがアイドル状態になると、プロセス プールの外で待機しているタスクが作業のためにプロセス プールに投入されます。


Close 関数と join 関数のデモ

上記のスクリプトでは、time.sleep(15)

ヘルプメインプロセスを 15 秒間ブロックして再度終了することで、プロセスプールに work() 関数のループタスクを完了するのに十分な時間を与えます。

time.sleep(15) がない場合はどうすればよいでしょうか? 実際、ここでプロセスの join 関数を使用できます。ただし、プロセスの join() 関数は通常、プロセス プールが閉じられるときに使用される (close 関数 ) ことも上で説明しました。次に、上記のスクリプトの time.sleep(15)join() 関数に置き換えて試してみます。

サンプル コードは次のとおりです。

# coding:utf-8import osimport timeimport multiprocessingdef work(count):    
# 定义一个 work 函数,打印输出 每次执行的次数 与 该次数的进程号
    print('\'work\' 函数 第 {} 次执行,进程号为 {}'.format(count, os.getpid()))
    time.sleep(3)
    # print('********')if __name__ == '__main__':
    pool = multiprocessing.Pool(3)      # 定义进程池的进程数量,同一时间每次执行最多3个进程
    for i in range(21):
        pool.apply_async(func=work, args=(i,))      
        # 传入的参数是元组,因为我们只有一个 i 参数,所以我们要写成 args=(i,)

    # time.sleep(15) 
    pool.close()
    pool.join()

実行結果は次のとおりです。


Python プロセス プールとプロセス ロックの詳細については、こちらをご覧ください。

上のアニメーション、work() 関数のタスクとプロセス プール内のプロセスは、time.sleep(15) を使用した結果と一致しています。

PS: メインプロセスが常に実行され、終了しない場合。そうすれば、close() 関数や join() 関数 を追加する必要がなく、タスクが到着して実行されるまでプロセス プールを開始したままにすることができます。

後からWEB開発を学んだ後は、メインプロセスから抜け出さずに作業するのが当たり前です。長時間実行する必要があり、閉じられないタスクもありますが、1 回限りの実行スクリプトしかない場合は、close() 関数と join() 関数を追加する必要があります プロセス プール内のすべてのタスクが完了した後でメイン プロセスが完了するようにします。再度終了します。もちろん、メインプロセスが閉じられると、新しいタスクは受け入れられなくなり、プロセスプールの終わりを意味します。


別の例を見て、work 関数 に return を追加してみましょう。

ここで質問があるかもしれません。前章のプロセスに関するナレッジポイントでは、プロセスは戻り値を取得できませんと明示されているため、ここでは work()# とします。 ## 関数に追加された return にはどのような意味がありますか?

其实不然,在我们的使用进程池的 apply_async 方法时,是通过异步的方式实现的,而异步是可以获取返回值的。针对上述脚本,我们在 for循环中针对每一个异步 apply_async 添加一个变量名,从而获取返回值。

示例代码如下:

# coding:utf-8import osimport timeimport multiprocessingdef work(count):    # 定义一个 work 函数,打印输出 每次执行的次数 与 该次数的进程号
    print('\'work\' 函数 第 {} 次执行,进程号为 {}'.format(count, os.getpid()))
    time.sleep(3)
    return '\'work\' 函数 result 返回值为:{}, 进程ID为:{}'.format(count, os.getpid())if __name__ == '__main__':
    pool = multiprocessing.Pool(3)      # 定义进程池的进程数量,同一时间每次执行最多3个进程
    results = []
    for i in range(21):
        result = pool.apply_async(func=work, args=(i,))      # 传入的参数是元组,因为我们只有一个 i 参数,所以我们要写成 args=(i,)
        results.append(result)

    for result in results:
        print(result.get())     # 可以通过这个方式返回 apply_async 的返回值,
                                # 通过这种方式也不再需要 使用 close()、join() 函数就可以正常执行。

    # time.sleep(15)      # 这里的休眠时间是必须要加上的,否则我们的进程池还未运行,主进程就已经运行结束,对应的进程池也会关闭。
    # pool.close()
    # pool.join()

运行结果如下:


Python プロセス プールとプロセス ロックの詳細については、こちらをご覧ください。

从运行结果可以看出,首先 work() 函数被线程池的线程执行了一遍,当第一组任务执行完毕紧接着执行第二次线程池任务的时候,打印输出了 apply_async 的返回值,证明返回值被成功的返回了。然后继续下一组的任务…

这些都是主要依赖于 异步 ,关于 异步 的更多知识会在 异步 的章节进行详细的介绍。


进程锁

 进程锁的概念

锁:大家都知道,我们可以给一个大门上锁。

结合这个场景来举一个例子:比如现在有多个进程同时冲向一个 "大门" ,当前门内是没有 "人"的(其实就是进程),锁也没有锁上。当有一个进程进去之后并且把 “门” 锁上了,这时候门外的那些进程是进不来的。在门内的 “人” ,可以在 “门” 内做任何事情且不会被干扰。当它出来之后,会解开门锁。这时候又有一个 “人” 进去了门内,并且重复这样的操作,这就是 进程锁。它可以让锁后面的工作只能被一个任务来处理,只有它解锁之后下一个任务才会进入,这就是 “锁” 的概念。

进程锁 就是仅针对于 进程 有效的锁,当进程的任务开始之后,就会被上一把 “锁”;与之对应的是 线程锁 ,它们的原理几乎是一样的。

进程锁的加锁与解锁

进程锁的使用方法:

通过 multiprocessing 导入 Manager 类

from multiprocessing import Manager

然后实例化 Manager

manager = Manager()

再然后通过实例化后的 manager 调用 它的 Lock() 函数

lock = manager.Lock()

接下来,就需要操作这个 lock 对象的函数

函数名 介绍 参数 返回值
acquire 上锁
release 解锁(开锁)

代码示例如下:

# coding:utf-8import osimport timeimport multiprocessingdef work(count, lock):    # 定义一个 work 函数,打印输出 每次执行的次数 与 该次数的进程号,增加线程锁。
    lock.acquire()        # 上锁
    print('\'work\' 函数 第 {} 次执行,进程号为 {}'.format(count, os.getpid()))
    time.sleep(3)
    lock.release()        # 解锁
    return '\'work\' 函数 result 返回值为:{}, 进程ID为:{}'.format(count, os.getpid())if __name__ == '__main__':
    pool = multiprocessing.Pool(3)      # 定义进程池的进程数量,同一时间每次执行最多3个进程
    manager = multiprocessing.Manager()
    lock = manager.Lock()
    results = []
    for i in range(21):
        result = pool.apply_async(func=work, args=(i, lock))      # 传入的参数是元组,因为我们只有一个 i 参数,所以我们要写成 args=(i,)
        # results.append(result)


    # time.sleep(15)      # 这里的休眠时间是必须要加上的,否则我们的进程池还未运行,主进程就已经运行结束,对应的进程池也会关闭。
    pool.close()
    pool.join()

执行结果如下:


Python プロセス プールとプロセス ロックの詳細については、こちらをご覧ください。

从上图中,可以看到每一次只有一个任务会被执行。由于每一个进程会被阻塞 3秒钟,所以我们的进程执行的非常慢。这是因为每一个进程进入到 work() 函数中,都会执行 上锁、阻塞3秒、解锁 的过程,这样就完成了一个进程的工作。下一个进程任务开始,重复这个过程… 这就是 进程锁的概念


其实进程锁还有很多种方法,在 multiprocessing 中有一个直接使用的锁,就是 ``from multiprocessing import Lock。这个Lock的锁使用和我们刚刚介绍的Manager` 的锁的使用有所区别。(这里不做详细介绍,感兴趣的话可以自行拓展一下。)

的使用可以让我们对某个任务 在同一时间只能对一个进程进行开发,但是 锁也不可以乱用 。因为如果某些原因造成 锁没有正常解开 ,就会造成死锁的现象,这样就无法再进行操作了。

因为 锁如果解不开 ,后面的任务也就没有办法继续执行任务,所以使用锁一定要谨慎。

推荐学习:python视频教程

以上がPython プロセス プールとプロセス ロックの詳細については、こちらをご覧ください。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はcsdn.netで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。