Pythonでのマルチスレッド[コーディング例付き]
公開: 2020-11-30Pythonの基本的な知識を習得した後の次のステップは、コードの改善と高速化です。 マルチスレッドは、「スレッド」を使用してその最適化を実現するための1つの方法です。 これらのスレッドは何ですか? そして、これらはプロセスとどのように異なりますか? 確認してみましょう。
このチュートリアルを終了するまでに、次の知識が身に付きます。
- スレッドとプロセスとは何ですか?
- マルチスレッドはどのように実現されますか?
- その制限は何ですか?
- マルチスレッドを使用する場合
世界のトップ大学からオンラインデータサイエンスコースを学びましょう。 エグゼクティブPGプログラム、高度な証明書プログラム、または修士プログラムを取得して、キャリアを早急に進めましょう。
目次
Pythonのスレッド
マルチタスクを考えるとき、並列実行を考えます。 マルチスレッドは厳密には並列実行ではありません。 スレッドは、独立して実行されているプログラムのさまざまな部分の実行フローの個別のエンティティと考えることができます。 つまり、基本的に、スレッドは並列で実行されませんが、Pythonはあるスレッドから別のスレッドに非常に高速に切り替わるため、並列であるように見えます。
一方、プロセスは厳密に並列であり、異なるコアで実行されるため、実行が高速になります。 スレッドは異なるプロセッサで実行することもできますが、技術的には並列で実行されません。
そして、スレッドが並列に実行されない場合、どのようにして処理を高速化するのか考えていますか? 答えは、必ずしも処理が速くなるとは限らないということです。 マルチスレッドは、スレッドによって処理が高速化されるタスクで特に使用されます。
スレッドのすべての情報は、スレッド制御ブロック(TCB)に含まれています。 TCBは、次の主要部分で構成されています。
- 一意のTID–スレッド識別子
- プロセス内のスレッドのスタックを指すスタックポインタ
- スレッドによって現在実行されている命令のアドレスを格納するプログラムカウンター
- スレッドの状態(実行中、準備完了、待機中、開始中、または完了済み)
そうは言っても、プロセスには、コード、データ、およびすべてのファイルを共有する複数のスレッドを含めることができます。 また、すべてのスレッドには、アクセスできる独自のレジスタとスタックがあります。
ここで、スレッドが共通のデータとコードを使用している場合、他のスレッドを妨げることなく、どのようにしてすべてのスレッドがそれを使用できるのか疑問に思うかもしれません。 これは、このチュートリアルの後半で説明するマルチスレッドの最大の制限です。
コンテキストスイッチング
上記のように、スレッドは並列に実行されませんが、結果として実行されます。 したがって、1つのスレッドT1が実行を開始すると、他のすべてのスレッドは待機モードのままになります。 T1の実行が完了して初めて、キューに入れられた他のスレッドの実行を開始できます。 Pythonは、あるスレッドから別のスレッドに非常に高速に切り替わるため、並列実行のように見えます。 この切り替えは、「コンテキスト切り替え」と呼ばれるものです。
マルチスレッドプログラミング
スレッドを使用してキューブおよびスクエア操作を実行する以下のコードについて考えてみます。
スレッドのインポート def cuber (n) : print( “ Cube:{}” .format(n * n * n)) def squarer (n) : print( “ Square:{}” .format(n * n)) if __name__ == “ __main __” : #スレッドを作成する t1 = threading.Thread(target = squarer、args =( 5 、)) t2 = threading.Thread(target = cube、args =( 5 、)) #スレッドt1を開始します t1.start() #スレッドt2を開始します t2.start() #t1が完了するまで待つ t1.join() #t2が完了するまで待つ t2.join() #両方のスレッドが完了しました print( “完了!” ) |
#出力: スクエア: 25 キューブ: 125 終わり! |
それでは、コードを理解してみましょう。
まず、すべてのタスクを担当するスレッドモジュールをインポートします。 メイン内では、Threadクラスのサブクラスを作成して2つのスレッドを作成します。 そのスレッドで実行する必要のある関数であるターゲットと、それらの関数に渡す必要のある引数を渡す必要があります。
スレッドが宣言されたら、それらを開始する必要があります。 これは、スレッドでstartメソッドを呼び出すことによって行われます。 開始すると、メインプログラムはスレッドが処理を終了するのを待つ必要があります。 waitメソッドを使用して、メインプログラムを一時停止し、スレッドT1とT2の実行が終了するのを待ちます。
必読:初心者のためのPythonの課題
スレッドの同期
上で説明したように、スレッドは並列で実行されず、代わりにPythonが相互に切り替わります。 したがって、奇妙な動作を回避するために、スレッド間で正しく同期することが非常に重要です。
競合状態
同じプロセス下にあるスレッドは、共通のデータとファイルを使用するため、複数のスレッド間のデータの「競争」につながる可能性があります。 したがって、データの一部が複数のスレッドによってアクセスされる場合、そのデータは両方のスレッドによって変更され、得られる結果は期待どおりにはなりません。 これは競合状態と呼ばれます。
したがって、同じデータにアクセスできる2つのスレッドがある場合、その特定のスレッドが実行されているときに、両方がそのデータにアクセスして変更できます。 したがって、T1が実行を開始して一部のデータを変更すると、T2はスリープ/待機モードになります。 次に、T1は実行を停止し、スリープモードに入り、T2に制御を渡します。T2も同じデータにアクセスできます。 そのため、T2は同じデータを変更および上書きするため、T1が再開したときに問題が発生します。

スレッド同期の目的は、この競合状態が発生しないようにし、コードのクリティカルセクションにスレッドが同期して一度に1つずつアクセスするようにすることです。
ロック
競合状態とその結果を解決および防止するために、スレッドモジュールは、スレッドの同期を支援するためにセマフォを使用するLockクラスを提供します。 セマフォはバイナリフラグに他なりません。 それらを、「従事している」(1に相当)または「従事していない」(0に相当)の値を持つ電話ブースの「従事している」サインと見なします。 したがって、スレッドがロックのあるコードのセグメントに遭遇するたびに、ロックがすでに1つの状態にあるかどうかを確認する必要があります。 そうである場合、それを使用できるように、0になるまで待機する必要があります。
Lockクラスには2つの主要なメソッドがあります。
- Acquisition([blocking]) : acquireメソッドは、パラメーターブロッキングをTrueまたはFalseとして受け取ります。 スレッドT1のロックが、ブロッキングをTrueとして開始された場合、コードのクリティカルセクションが別のスレッドT2によってロックされるまで、待機するか、ブロックされたままになります。 もう一方のスレッドT2がロックを解放すると、スレッドT1はロックを取得し、 Trueを返します。
一方、スレッドT1のロックがパラメータブロッキングをFalseとして開始された場合、クリティカルセクションがスレッドT2によってすでにロックされていれば、スレッドT1は待機しないか、ブロックされたままになります。 ロックされていると見なされた場合は、すぐにFalseを返し、終了します。 ただし、コードが別のスレッドによってロックされていない場合は、ロックを取得してTrueを返します。
release() :ロックでreleaseメソッドが呼び出されると、ロックのロックが解除され、Trueが返されます。 また、ロックが解除されるのを待っているスレッドがあるかどうかもチェックします。 ある場合は、そのうちの1つだけがロックにアクセスできるようになります。
ただし、ロックがすでにロック解除されている場合は、ThreadErrorが発生します。
デッドロック
複数のロックを処理するときに発生するもう1つの問題は、デッドロックです。 デッドロックは、さまざまな理由でスレッドによってロックが解放されない場合に発生します。 次のような簡単な例を考えてみましょう。
スレッドのインポート l = threading.Lock() #1回目の取得前 l.acquire() #2回目の取得前 l.acquire() #ロックを2回取得しました |
上記のコードでは、acquireメソッドを2回呼び出していますが、最初に取得した後で解放しません。 したがって、Pythonが2番目のacquireステートメントを検出すると、前のロックを解放したことがないため、Pythonは無期限に待機モードになります。
これらのデッドロック状態は、気付かないうちにコードに忍び寄る可能性があります。 リリース呼び出しを含めても、コードが途中で失敗する可能性があり、リリースが呼び出されることはなく、ロックはロックされたままになります。 これを克服する1つの方法は、コンテキストマネージャーとも呼ばれるwith –asステートメントを使用することです。 with – asステートメントを使用すると、何らかの理由で処理が終了または失敗すると、ロックが自動的に解放されます。
読む: Pythonプロジェクトのアイデアとトピック
行く前に
前に説明したように、マルチスレッドは実際には並列で実行されないため、すべてのアプリケーションで役立つわけではありません。 ただし、マルチスレッドの主な用途は、データがロードされるのを待っている間、CPUがアイドル状態になっているI/Oタスク中です。 このCPUのアイドル時間は他のタスクで利用されるため、マルチスレッドはここで重要な役割を果たし、最適化に最適です。
データサイエンスについて知りたい場合は、IIIT-B&upGradのデータサイエンスのエグゼクティブPGプログラムをチェックしてください。これは、働く専門家向けに作成され、10以上のケーススタディとプロジェクト、実践的なハンズオンワークショップ、業界の専門家とのメンターシップを提供します。1業界のメンターとの1対1、400時間以上の学習、トップ企業との仕事の支援。
Pythonのスレッドとは何ですか?
スレッドは、Pythonでの実行をスケジュールできるプロセス内のエンティティです。 素人の言葉で言えば、スレッドはコンピューターによって実行される計算プロセスです。 これは、開発者が他のスクリプトとは独立して実行できる、プログラム内のそのような命令のセットです。 スレッドを使用すると、並列処理を使用してアプリケーションの速度を上げることができます。 これは、タスクを並行して操作できるようにする軽量プロセスです。 スレッドは独立して動作し、CPUの使用を最大化するため、CPUのパフォーマンスが向上します。
Pythonでのマルチスレッドの使用は何ですか?
マルチスレッドはPythonプログラミングのスレッド技術であり、CPUを使用してスレッドを高速に切り替える(コンテキスト切り替えと呼ばれる)ことで、多くのスレッドを同時に動作させることができます。 タスクを複数の個別のセクションに分割できる場合は、マルチスレッドを利用します。 たとえば、データを取得し、そのクエリを多数の個別のクエリに分割するために、複雑なデータベースクエリを実行する必要があるとします。 その場合、各クエリにスレッドを割り当て、それらをすべて並行して実行することが望ましいでしょう。
スレッド同期とは何ですか?
スレッドの同期は、2つ以上の同時プロセスまたはスレッドがプログラムの重要な部分を同時に実行しないことを保証する方法として説明されています。 同期方法は、重要なセクションへのプロセスのアクセスを制御するために使用されます。 プログラム内で2つ以上のスレッドを開始すると、複数のスレッドが同じリソースにアクセスしようとする可能性があり、同時実行の問題により予期しない結果が生じる可能性があります。 たとえば、多くのスレッドが同じファイル内に書き込もうとすると、スレッドの1つがデータを上書きできるため、または1つのスレッドが開いていて、別のスレッドが同じファイルを閉じているときに、データが破損する可能性があります。