ホーム テクノロジー 開発 Python スレッディング: 概要

Python スレッディング: 概要


このチュートリアルでは、Python の組み込みスレッドモジュールを使用して、Python のマルチスレッド機能を調べる方法を学びます。

プロセスとスレッドの基本から始めて、同時実行性と並列性の概念を理解しながら、Python でマルチスレッドがどのように機能するかを学びます。次に、組み込みのthreadingモジュールを使用して Python で 1 つ以上のスレッドを開始および実行する方法を学びます。

始めましょう。

プロセスとスレッド: 違い

プロセスとは何ですか?

プロセスとは、実行する必要があるプログラムのインスタンスです。

Python スクリプトや Chrome などの Web ブラウザからビデオ会議アプリケーションまで、何でも構いません。マシン上でタスク マネージャーを起動し、 [パフォーマンス] –> [CPU]に移動すると、現在 CPU コアで実行されているプロセスとスレッドを確認できます。

CPU-proc-スレッド
CPU-proc-スレッド

プロセスとスレッドを理解する

内部的には、プロセスには、プロセスに対応するコードとデータを保存する専用メモリがあります。

プロセスは 1 つ以上のスレッドで構成されます。スレッドは、オペレーティング システムが実行できる最小の命令シーケンスであり、実行の流れを表します。

各スレッドには独自のスタックとレジスタがありますが、専用のメモリはありません。プロセスに関連付けられたすべてのスレッドがデータにアクセスできます。したがって、データとメモリはプロセスのすべてのスレッドで共有されます。

プロセスとスレッド
プロセスとスレッド

N 個のコアを備えた CPU では、N 個のプロセスが同時に並行して実行できます。ただし、同じプロセスの 2 つのスレッドは並行して実行することはできませんが、同時に実行することはできます。次のセクションでは、同時実行性と並列性の概念について説明します。

これまでに学んだことに基づいて、プロセスとスレッドの違いをまとめてみましょう。

特徴プロセス
メモリ専用メモリ共有メモリ
実行モード並行、同時同時;しかし平行ではない
実行処理担当者オペレーティング·システムCPython インタープリター

Python でのマルチスレッド化

Python では、 グローバル インタープリター ロック (GIL) により、いつでも1 つのスレッドだけがロックを取得して実行できることが保証されます。すべてのスレッドが実行するにはこのロックを取得する必要があります。これにより、どの時点でも 1 つのスレッドのみが実行可能になり、同時マルチスレッドが回避されます。

たとえば、同じプロセスの 2 つのスレッドt1t2について考えてみましょう。スレッドは同じデータを共有するため、 t1が特定の値kを読み取っているときに、 t2同じ値kを変更する可能性があります。これにより、デッドロックや望ましくない結果が発生する可能性があります。ただし、いつでもロックを取得して実行できるスレッドは 1 つだけです。したがって、GIL はスレッドの安全性も保証します。

では、Python でマルチスレッド機能を実現するにはどうすればよいでしょうか?これを理解するために、並行性と並列性の概念について説明します。

同時実行性と並列性: 概要

複数のコアを備えた CPU を考えてみましょう。下の図では、CPU に 4 つのコアがあります。これは、いつでも 4 つの異なる操作を並行して実行できることを意味します。

プロセスが 4 つある場合、各プロセスは 4 つのコアのそれぞれで独立して同時に実行できます。各プロセスに 2 つのスレッドがあると仮定します。

マルチコア並列処理
マルチコア並列処理

スレッドの仕組みを理解するために、マルチコア プロセッサ アーキテクチャからシングルコア プロセッサ アーキテクチャに切り替えてみましょう。前述したように、特定の実行インスタンスでアクティブにできるスレッドは 1 つだけです。ただし、プロセッサ コアはスレッドを切り替えることができます。

コード
コード

たとえば、I/O バインドされたスレッドは、ユーザー入力の読み取り、データベースの読み取り、ファイル操作などの I/O 操作を待機することがよくあります。この待機時間中に、I/O バウンドのスレッドはロックを解放して、他のスレッドが実行できるようにします。待機時間は、 n秒間スリープするなどの単純な操作にすることもできます。

要約: 待機操作中に、スレッドはロックを解放し、プロセッサ コアが別のスレッドに切り替えられるようにします。待機期間が完了すると、前のスレッドが実行を再開します。プロセッサ コアがスレッド間を同時に切り替えるこのプロセスにより、マルチスレッド化が容易になります。 ✅

アプリケーションにプロセスレベルの並列処理を実装する場合は、代わりにマルチプロセッシングの使用を検討してください。

Python スレッディング モジュール: 最初のステップ

Python には、Python スクリプトにインポートできるthreadingモジュールが付属しています。

 import threading

Python でスレッド オブジェクトを作成するには、 Threadコンストラクターthreading.Thread(...)を使用できます。これは、ほとんどのスレッド実装に十分な一般的な構文です。

 threading.Thread(target=...,args=...)

ここ、

  • targetは、Python 呼び出し可能オブジェクトを示すキーワード引数です。
  • args 、ターゲットが受け取る引数のタプルです。

このチュートリアルのコード例を実行するには、Python 3.x が必要です。 コードをダウンロードして、手順に従ってください。

Python でスレッドを定義して実行する

ターゲット関数を実行するスレッドを定義しましょう。

ターゲット関数はsome_funcです。

 import threading
import time

def some_func():
    print("Running some_func...")
    time.sleep(2)
    print("Finished running some_func.")

thread1 = threading.Thread(target=some_func)
thread1.start()
print(threading.active_count())

上記のコード スニペットが何を行うかを解析してみましょう。

  • threadingtimeモジュールをインポートします。
  • 関数some_funcは説明的なprint()ステートメントがあり、2 秒間のスリープ操作が含まれています。 time.sleep(n)は関数をn秒間スリープさせます。
  • 次に、ターゲットをsome_funcとしてスレッドthread_1を定義します。 threading.Thread(target=...)スレッド オブジェクトを作成します。
  • : 関数呼び出しではなく、関数の名前を指定します。 some_func()ではなくsome_funcを使用してください。
  • スレッド オブジェクトを作成してもスレッドは開始されません。スレッド オブジェクトでstart()メソッドを呼び出すと実行されます。
  • アクティブなスレッドの数を取得するには、 active_count()関数を使用します。

Python スクリプトはメイン スレッドで実行されており、関数some_funcを実行する別のスレッド ( thread1 ) を作成しています。したがって、出力に見られるように、アクティブなスレッド数は 2 です。

 # Output
Running some_func...
2
Finished running some_func.

出力を詳しく見ると、 thread1の開始時に最初の print ステートメントが実行されることがわかります。ただし、スリープ操作中、プロセッサはthread1の実行が完了するのを待たずに、メイン スレッドに切り替えてアクティブなスレッドの数を出力します。

スレッド1-ex
スレッド1-ex

スレッドの実行が終了するのを待機しています

thread1に実行を終了させたい場合は、スレッドの開始後にjoin()メソッドを呼び出すことができます。これを行うと、 thread1メイン スレッドに切り替えずに実行が終了するまで待機します。

 import threading
import time

def some_func():
    print("Running some_func...")
    time.sleep(2)
    print("Finished running some_func.")

thread1 = threading.Thread(target=some_func)
thread1.start()
thread1.join()
print(threading.active_count())

ここで、アクティブなスレッド数を出力する前に、 thread1の実行が終了しました。したがって、メインスレッドのみが実行されています。つまり、アクティブなスレッド数は 1 つです。 ✅

 # Output
Running some_func...
Finished running some_func.
1

Python で複数のスレッドを実行する方法

次に、2 つの異なる関数を実行する 2 つのスレッドを作成しましょう。

ここで、 count_down数値を引数に取り、その数値から 0 までカウントダウンする関数です。

 def count_down(n):
    for i in range(n,-1,-1):
        print(i)

count_upを定義します。これは、ゼロから指定された数値までカウントする別の Python 関数です。

 def count_up(n):
    for i in range(n+1):
        print(i)

📑 range()関数range(start, stop, step)構文で使用する場合、エンドポイントstopデフォルトで除外されます。

– 特定の数値からゼロまでカウントダウンするには、負のstep値 -1 を使用し、ゼロが含まれるようにstop値を -1 に設定します。

– 同様に、 nまでカウントするには、 stop値をn + 1に設定する必要があります。 startstepのデフォルト値はそれぞれ 0 と 1 であるため、 range(n + 1)を使用して 0 から n までのシーケンスを取得できます。

次に、関数count_downcount_upをそれぞれ実行する 2 つのスレッドthread1thread2を定義します。両方の関数にprintステートメントとsleep操作を追加します。

スレッド オブジェクトを作成するときは、ターゲット関数の引数をタプルとしてargsパラメーターに指定する必要があることに注意してください。両方の関数 ( count_downcount_up ) は 1 つの引数を取るため、値の後に明示的にコンマを挿入する必要があります。これにより、後続の要素はNoneとして推論されるため、引数は引き続きタプルとして渡されます。

 import threading
import time

def count_down(n):
    for i in range(n,-1,-1):
        print("Running thread1....")
        print(i)
        time.sleep(1)


def count_up(n):
    for i in range(n+1):
        print("Running thread2...")
        print(i)
        time.sleep(1)

thread1 = threading.Thread(target=count_down,args=(10,))
thread2 = threading.Thread(target=count_up,args=(5,))
thread1.start()
thread2.start()

出力では次のようになります。

  • 関数count_up thread2で実行され、0 から開始して 5 までカウントします。
  • count_down関数はthread1で実行され、10 から 0 までカウントダウンします。
 # Output
Running thread1....
10
Running thread2...
0
Running thread1....
9
Running thread2...
1
Running thread1....
8
Running thread2...
2
Running thread1....
7
Running thread2...
3
Running thread1....
6
Running thread2...
4
Running thread1....
5
Running thread2...
5
Running thread1....
4
Running thread1....
3
Running thread1....
2
Running thread1....
1
Running thread1....
0

thread1thread2どちらも待機操作 (スリープ) を必要とするため、交互に実行されていることがわかります。 count_up関数が 5 までカウントを終了すると、 thread2はアクティブでなくなります。したがって、 thread1のみに対応する出力が得られます。

まとめ

このチュートリアルでは、Python の組み込みスレッド モジュールを使用してマルチスレッドを実装する方法を学習しました。主な要点を以下にまとめます。

  • Threadコンストラクターを使用して、スレッド オブジェクトを作成できます。 threading.Thread(target=<callable>,args=(<tuple of args>)) を使用すると、 argsで指定された引数を使用してターゲット呼び出し可能オブジェクトを実行するスレッドが作成されます。
  • Python プログラムはメイン スレッドで実行されるため、作成するスレッド オブジェクトは追加のスレッドです。 active_count()関数を呼び出すと、任意のインスタンスでアクティブなスレッドの数が返されます。
  • スレッド オブジェクトのstart()メソッドを使用してスレッドを開始し、 join()メソッドを使用して実行が終了するまで待機できます。

待機時間を調整したり、別の I/O 操作を試したりすることで、追加の例をコーディングできます。今後の Python プロジェクトでは必ずマルチスレッドを実装してください。コーディングを楽しんでください!🎉

「 Python スレッディング: 概要」についてわかりやすく解説!絶対に観るべきベスト2動画

Pythonでマルチスレッドプログラミングをやる方法【threading, Thread】
[シリコンバレー流プログラミング] スレッドとマルチプロセスのIOバウンドとCPUバウンドを理解する