マルチスレッド化とマルチプロセッシングは、同時実行と並列化を実現する最も一般的な 2 つの方法ですが、これらの違いを理解している開発者は多くなく、どちらをいつ使用するかを効果的に選択できません。
この記事では、マルチスレッドとマルチプロセッシングの違いと、何を使用するかを決定する方法と、Python でそれを実装する方法について説明します。
スレッドは独立した実行フローです。基本的には、並行して実行できるプロセスの軽量の個々のコンポーネントと見なすことができます。スレッド化は、通常、オペレーティング システムによって提供される機能です。同じメモリ空間を共有するプロセス内に複数のスレッドが存在する可能性があります。これは、実行されるコードとプログラムで宣言された変数を相互に共有することを意味します。
これをよりよく理解するために、ラップトップで現在実行されているプログラムの例を考えてみましょう。おそらく、ブラウザで複数のタブを開いた状態でこの記事を読んでいるでしょう。その間、音楽を聴くために Spotify デスクトップ アプリを開いています。現在、ブラウザーと Spotify デスクトップ アプリは、複数のプロセスまたはスレッドを使用して並列処理を実現できる 2 つの別個のプロセスのようなものです。そのため、ブラウザーのさまざまなタブがさまざまなスレッドで実行される可能性があります。同様に、Spotify は 1 つのスレッドを使用して音楽を再生し、別のスレッドを使用してインターネットからお気に入りの曲をダウンロードし、3 つ目のスレッドを使用してユーザー インターフェイスを表示できます。そして、これはマルチスレッドと呼ばれます。
マルチスレッドは、名前が示すように、複数のスレッドを同時に実行できるタスクまたは操作です。これは、複数のタスクを同時に立て続けに合理化し、メイン スレッドを使用して複数のスレッド間でリソースをすばやく簡単に共有できるようにする一般的な手法です。
次の図は、Python でのマルチスレッドについて説明しています。
Python は線形言語ですが、Threading Python モジュールを使用して、Python でのマルチスレッドの概念を理解し、実装することができます。 threading モジュールは、より多くの処理能力が必要な場合に使用できる複数のスレッドを簡単に生成する直感的な API を提供します。
以下に示すように使用できます。
import threading from queue import Queue import time def testThread(num): print num if __name__ == '__main__': for i in range(5): t = threading.Thread(target=testThread, arg=(i,)) t.start()
上記のコード スニペットでは、 target
が呼び出し可能なオブジェクトとして使用され、 args
は関数にパラメーターを渡し、スレッドをstart
するために使用されます。
さて、興味深いものがあります - ロックです。
プログラミングでは、スレッドに共通の変数をスレッドで変更または使用できるようにしたい場合がよくあります。ただし、これを行うには、Python でLockまたはGlobal Interpreter Lock (GIL)と呼ばれるものを使用する必要があります。
パイソンから
CPython では、グローバル インタープリター ロック( GIL ) は Python オブジェクトへのアクセスを保護するミューテックスであり、複数のスレッドが Python バイトコードを一度に実行するのを防ぎます。このロックが必要なのは、主に CPython のメモリ管理がスレッドセーフではないためです。
インタープリター レベルでは、Python は基本的に命令をシリアル化します。スレッドが関数を実行するには、まずグローバル ロックを取得する必要があります。一度にそのロックを取得できるスレッドは 1 つだけであるため、インタープリターは最終的に命令をシリアルに実行する必要があります。このアーキテクチャはメモリ管理をスレッドセーフにしますが、複数の CPU コアをまったく使用できません。
簡単に言えば、関数が変数を使用または変更する場合は常にその変数をロックし、他の関数がその特定の変数を使用または変更する場合は、その変数がロック解除されるまで待機する必要があります。
それぞれ変数を 1 ずつ反復する 2 つの関数を考えてみましょう。ロックを使用して、ある関数が変数を読み取り、計算を実行し、別の関数ができる前に変数に書き戻すことができるようにすることで、データの破損を防ぐことができます。
Python でのスレッド化は、CPU を集中的に使用する可能性のあるタスクよりも、I/O 操作やスクリプトの実行などのネットワークにバインドされたタスクに役立ちます。たとえば、Web スクレイピングの場合です。もう 1 つの例は、スレッド プールを使用してデータを並列に変換するTensorflowです。
これらのアプリケーション以外に、グラフィカル ユーザー インターフェイス (GUI) は常にマルチスレッドを使用して、アプリケーションの応答性と対話性を高めています。一般的な例は、ユーザーがテキストを入力するとすぐに画面に表示されるテキスト編集プログラムです。ここでは、一方のスレッドがユーザー入力を処理し、もう一方のスレッドがタスクを処理して表示します。スペルチェック、オートコンプリートなどの機能を追加するためにスレッドを追加できます。
スレッドについて詳しく説明したので、プロセスに移りましょう。
プロセスは、実行中のコンピュータ プログラムの単なるインスタンスです。各プロセスには、実行中の命令を格納するために使用される独自のメモリ空間と、コードの実行のためにアクセスまたは格納する必要があるデータがあります。このため、プロセスの生成は、スレッドに比べて時間がかかり遅くなります。
前述のように、デスクトップで複数のアプリケーションを実行している場合、各アプリケーションはプロセスであり、これらのプロセスを同時に実行している場合、マルチプロセッシングと呼ばれます。
マルチプロセッシングとは、複数の無関係なタスクを同時に実行するプロセッサの機能です。 Global Interpreter Lock (GIL) をバイパスして同時に実行できるプログラムを作成し、CPU コア全体を使用してタスクを効率的に実行できます。
マルチプロセッシングの概念はマルチスレッドとは根本的に異なりますが、Python での構文や使用法は非常に似ています。 Threading モジュールと同様に、Python には Multiprocessing モジュールがあり、さまざまなプロセスを生成するのに役立ちます。各プロセスには独自の Python インタープリターと GIL があります。
プロセスは同じメモリを共有しないため、同じメモリを同時に変更することはできず、デッドロックに陥ったり、データが破損したりするリスクを回避できます。
以下に示すように使用できます。
import multiprocessing def spawn(num): print(num) if __name__ == '__main__': for i in range(5): p = multiprocessing.Process(target=spawn, args=(i,)) p.start() p.join() # this line allows you to wait for processes
前にも説明したように、マルチプロセッシングは、タスクが CPU を大量に使用し、I/O 操作やユーザーの操作がない場合に適しています。
マルチプロセッシングとマルチスレッドの違い、メリット、デメリットをまとめたポイントを次に示します。
この議論から、次の結論を導き出すことができます。
Python のマルチプロセッシングとマルチスレッドがどのように動作し、それらがどのように比較されるかを理解したので、コードを効果的に記述し、さまざまな状況で 2 つのアプローチを適用することができます。
この記事がお役に立てば幸いです。読み続けます!