Concurrency-in-python-multiprocessing

提供:Dev Guides
移動先:案内検索

Pythonの同時実行性-マルチプロセッシング

この章では、マルチプロセッシングとマルチスレッドの比較に焦点を当てます。

マルチプロセッシング

これは、単一のコンピューターシステム内で2つ以上のCPUユニットを使用することです。 コンピューターシステムで利用可能なCPUコアを最大限に活用して、ハードウェアから最大限の可能性を引き出すのが最善の方法です。

マルチスレッド

CPUが複数のスレッドを同時に実行することでオペレーティングシステムの使用を管理する能力です。 マルチスレッドの主なアイデアは、プロセスを複数のスレッドに分割することで並列処理を実現することです。

次の表は、それらの間の重要な違いのいくつかを示しています-

Multiprocessing Multiprogramming
Multiprocessing refers to processing of multiple processes at same time by multiple CPUs. Multiprogramming keeps several programs in main memory at the same time and execute them concurrently utilizing single CPU.
It utilizes multiple CPUs. It utilizes single CPU.
It permits parallel processing. Context switching takes place.
Less time taken to process the jobs. More Time taken to process the jobs.
It facilitates much efficient utilization of devices of the computer system. Less efficient than multiprocessing.
Usually more expensive. Such systems are less expensive.

グローバルインタープリターロック(GIL)の影響を排除

並行アプリケーションを使用している間、Pythonには* GIL(Global Interpreter Lock)*と呼ばれる制限があります。 GILでは、CPUの複数のコアを使用することはできません。したがって、Pythonには真のスレッドは存在しないと言えます。 GILは相互排他ロックであり、物事をスレッドセーフにします。 言い換えれば、GILは、複数のスレッドがPythonコードを並行して実行するのを防ぐと言えます。 ロックは一度に1つのスレッドのみが保持でき、スレッドを実行する場合は、最初にロックを取得する必要があります。

マルチプロセッシングを使用すると、GILによる制限を効果的に回避できます-

  • マルチプロセッシングを使用することにより、複数のプロセスの機能を利用しているため、GILの複数のインスタンスを利用しています。
  • このため、プログラム内で一度に1つのスレッドのバイトコードを実行するという制限はありません。

Pythonでのプロセスの開始

次の3つの方法は、マルチプロセッシングモジュール内でPythonでプロセスを開始するために使用することができます-

  • Fork
  • スポーン
  • フォークサーバー

Forkでプロセスを作成する

Forkコマンドは、UNIXにある標準コマンドです。 子プロセスと呼ばれる新しいプロセスを作成するために使用されます。 この子プロセスは、親プロセスと呼ばれるプロセスと同時に実行されます。 これらの子プロセスも親プロセスと同一であり、親が使用できるすべてのリソースを継承します。 次のシステムコールは、フォークでプロセスを作成するときに使用されます-

  • * fork()*-一般的にカーネルに実装されているシステムコールです。 process.p>のコピーを作成するために使用されます
  • * getpid()*-このシステムコールは呼び出しプロセスのプロセスID(PID)を返します。

次のPythonスクリプトの例は、新しい子プロセスを作成し、子プロセスと親プロセスのPIDを取得する方法を理解するのに役立ちます-

import os

def child():
   n = os.fork()

   if n > 0:
      print("PID of Parent process is : ", os.getpid())

   else:
      print("PID of Child process is : ", os.getpid())
child()

出力

PID of Parent process is : 25989
PID of Child process is : 25990

Spawnを使用してプロセスを作成する

スポーンとは、何か新しいことを始めることを意味します。 したがって、プロセスの生成とは、親プロセスによる新しいプロセスの作成を意味します。 親プロセスは非同期に実行を継続するか、子プロセスが実行を終了するまで待機します。 プロセスを生成するには、次の手順に従います-

  • マルチプロセッシングモジュールのインポート。
  • オブジェクトプロセスの作成。
  • * start()*メソッドを呼び出してプロセスアクティビティを開始します。
  • プロセスが作業を終了するまで待機し、* join()*メソッドを呼び出して終了します。

次のPythonスクリプトの例は、3つのプロセスの生成に役立ちます

import multiprocessing

def spawn_process(i):
   print ('This is process: %s' %i)
   return

if __name__ == '__main__':
   Process_jobs = []
   for i in range(3):
   p = multiprocessing.Process(target = spawn_process, args = (i,))
      Process_jobs.append(p)
   p.start()
   p.join()

出力

This is process: 0
This is process: 1
This is process: 2

Forkserverでプロセスを作成する

Forkserverメカニズムは、Unixパイプを介したファイル記述子の受け渡しをサポートする選択されたUNIXプラットフォームでのみ使用可能です。 Forkserverメカニズムの動作を理解するには、次の点を考慮してください-

  • サーバーは、新しいプロセスを開始するためにForkserverメカニズムを使用してインスタンス化されます。
  • サーバーはコマンドを受信し、新しいプロセスを作成するためのすべての要求を処理します。
  • 新しいプロセスを作成するために、PythonプログラムはForkserverにリクエストを送信し、プロセスを作成します。 *最後に、この新しく作成されたプロセスをプログラムで使用できます。

Pythonのデーモンプロセス

Python* multiprocessing *モジュールを使用すると、デーモンオプションを使用してデーモンプロセスを実行できます。 デーモンプロセスまたはバックグラウンドで実行されているプロセスは、デーモンスレッドと同様の概念に従います。 バックグラウンドでプロセスを実行するには、デーモンフラグをtrueに設定する必要があります。 デーモンプロセスは、メインプロセスが実行されている限り実行を継続し、実行の終了後またはメインプログラムが強制終了されたときに終了します。

ここでは、デーモンスレッドで使用されているのと同じ例を使用しています。 唯一の違いは、モジュールを multithreading から multiprocessing に変更し、デーモンフラグをtrueに設定することです。 ただし、以下に示すように出力に変更があります-

import multiprocessing
import time

def nondaemonProcess():
   print("starting my Process")
   time.sleep(8)
   print("ending my Process")
def daemonProcess():
   while True:
   print("Hello")
   time.sleep(2)
if __name__ == '__main__':
   nondaemonProcess = multiprocessing.Process(target = nondaemonProcess)
   daemonProcess = multiprocessing.Process(target = daemonProcess)
   daemonProcess.daemon = True
   nondaemonProcess.daemon = False
   daemonProcess.start()
   nondaemonProcess.start()

出力

starting my Process
ending my Process

デーモンなしモードのプロセスには出力があるため、出力はデーモンスレッドによって生成されたものとは異なります。 したがって、実行中のプロセスの永続性を回避するために、メインプログラムの終了後にデーモンプロセスが自動的に終了します。

Pythonでのプロセスの終了

  • terminate()*メソッドを使用すると、プロセスを即座に強制終了または終了できます。 このメソッドを使用して、関数の助けを借りて作成された子プロセスを、実行を完了する直前に終了します。

import multiprocessing
import time
def Child_process():
   print ('Starting function')
   time.sleep(5)
   print ('Finished function')
P = multiprocessing.Process(target = Child_process)
P.start()
print("My Process has terminated, terminating main thread")
print("Terminating Child Process")
P.terminate()
print("Child Process successfully terminated")

出力

My Process has terminated, terminating main thread
Terminating Child Process
Child Process successfully terminated

出力は、Child_process()関数を使用して作成された子プロセスの実行前にプログラムが終了することを示しています。 これは、子プロセスが正常に終了したことを意味します。

Pythonで現在のプロセスを特定する

オペレーティングシステムのすべてのプロセスには、PIDと呼ばれるプロセスIDがあります。 Pythonでは、次のコマンドの助けを借りて現在のプロセスのPIDを見つけることができます-

import multiprocessing
print(multiprocessing.current_process().pid)

Pythonスクリプトの次の例は、メインプロセスのPIDと子プロセスのPIDを見つけるのに役立ちます-

import multiprocessing
import time
def Child_process():
   print("PID of Child Process is: {}".format(multiprocessing.current_process().pid))
print("PID of Main process is: {}".format(multiprocessing.current_process().pid))
P = multiprocessing.Process(target=Child_process)
P.start()
P.join()

出力

PID of Main process is: 9401
PID of Child Process is: 9402

サブクラスでプロセスを使用する

*threading.Thread* クラスをサブクラス化することでスレッドを作成できます。 さらに、 *multiprocessing.Process* クラスをサブクラス化してプロセスを作成することもできます。 サブクラスでプロセスを使用するには、次の点を考慮する必要があります-
  • Process クラスの新しいサブクラスを定義する必要があります。
  • * init(self [、args])*クラスをオーバーライドする必要があります。
  • run を実装するには、* run(self [、args])*メソッドのをオーバーライドする必要があります
  • start()メソッドを呼び出してプロセスを開始する必要があります。

import multiprocessing
class MyProcess(multiprocessing.Process):
   def run(self):
   print ('called run method in process: %s' %self.name)
   return
if __name__ == '__main__':
   jobs = []
   for i in range(5):
   P = MyProcess()
   jobs.append(P)
   P.start()
   P.join()

出力

called run method in process: MyProcess-1
called run method in process: MyProcess-2
called run method in process: MyProcess-3
called run method in process: MyProcess-4
called run method in process: MyProcess-5

Pythonマルチプロセッシングモジュール–プールクラス

Pythonアプリケーションで単純な並列*処理*タスクについて話す場合、マルチプロセッシングモジュールはPoolクラスを提供します。 Pool クラスの次のメソッドを使用して、メインプログラム内の子プロセスの数を増やすことができます。

apply()メソッド

このメソッドは、。ThreadPoolExecutor。。submit()メソッドに似ています。結果の準備ができるまでブロックします。

apply_async()メソッド

タスクの並列実行が必要な場合、apply_async()メソッドを使用してタスクをプールに送信する必要があります。 これは、すべての子プロセスが実行されるまでメインスレッドをロックしない非同期操作です。

map()メソッド

  • apply()メソッドと同様に、結果の準備ができるまでブロックします。 これは、反復可能なデータをいくつかのチャンクに分割し、個別のタスクとしてプロセスプールに送信する組み込みの map()*関数と同等です。

map_async()メソッド

  • apply_async() apply()メソッドに対するものであるため、これは map()*メソッドのバリアントです。 結果オブジェクトを返します。 結果の準備が整うと、呼び出し可能オブジェクトが適用されます。 呼び出し可能オブジェクトはすぐに完了する必要があります。そうしないと、結果を処理するスレッドがブロックされます。

次の例は、並列実行を実行するプロセスプールの実装に役立ちます。 multiprocessing.Pool メソッドを介して* square()関数を適用することにより、数の2乗の簡単な計算が実行されました。 入力は0〜4の整数のリストであるため、 pool.map()を使用して5を送信しました。 結果は *p_outputs に保存され、出力されます。

def square(n):
   result = n*n
   return result
if __name__ == '__main__':
   inputs = list(range(5))
   p = multiprocessing.Pool(processes = 4)
   p_outputs = pool.map(function_square, inputs)
   p.close()
   p.join()
   print ('Pool :', p_outputs)

出力

Pool : [0, 1, 4, 9, 16]