Python
 Computer >> コンピューター >  >> プログラミング >> Python

Pythonのthreadingモジュールで学ぶスレッドベースの並列処理

スレッドとは何か

コンピュータサイエンスにおけるスレッド(Thread)とは、オペレーティングシステム(OS)の一部であるスケジューラによって独立して管理できる、一連の命令(処理の手順)のことです。

スレッディングの主な役割は、複数のスレッドを同時に実行することです。ここでいうスレッドとは、プログラム内の個々のタスクや関数呼び出しを指します。複数のスレッドが同時に動作するといっても、それは別々のマシン上で実行されるという意味ではなく、同じプロセス内で並行して処理が進むことを意味します。

マルチスレッディングが活用される主なケース

  • サブプログラムの出力結果を、メインプログラムの結果と統合する必要がある場合
  • メインプログラムの中に、互いに比較的独立したコード断片が含まれている場合

threadingモジュールの主な関数

Pythonには標準ライブラリとしてthreadingモジュールが用意されており、スレッドに対する強力かつ高水準なサポートを提供しています。このモジュールには、スレッドに関する各種データを取得・操作するための関数が多数定義されています。

threading.active_count()

現在生存しているThreadオブジェクトの数を返します。戻り値は、enumerate()が返すリストの長さと一致します。

threading.current_thread()

呼び出し元のスレッドに対応する、現在のThreadオブジェクトを返します。

threading.get_ident()

現在のスレッドの「スレッド識別子」を返します。戻り値は0以外の整数です。

threading.enumerate()

現在生存しているすべてのThreadオブジェクトのリストを返します。デーモンスレッドやcurrent_thread()が生成するダミースレッド、メインスレッドは含まれますが、終了済みのスレッドやまだ開始されていないスレッドは除外されます。

threading.main_thread()

メインスレッドのThreadオブジェクトを返します。

threading.settrace(func)

threadingモジュールから起動されるすべてのスレッドに対してトレース関数を設定します。run()メソッドが呼び出される前に、この関数が各スレッドのsys.settrace()に渡されます。

threading.setprofile(func)

threadingモジュールから起動されるすべてのスレッドに対してプロファイル関数を設定します。run()メソッドが呼び出される前に、この関数が各スレッドのsys.setprofile()に渡されます。

threading.stack_size([size])

新しいスレッドを作成する際に使用されるスレッドスタックのサイズを返します。引数sizeを指定すると、そのサイズに変更できます。

threading.TIMEOUT_MAX

ブロッキング関数(Lock.acquire()、RLock.acquire()、Condition.wait()など)のtimeout引数に許容される最大値を表す定数です。

サンプルコード

以下は、これらの関数を実際に使用した例です。threading.Threadクラスを継承したカスタムスレッドクラスを定義し、複数のスレッドを起動しています。

import threading

def trace_function():
    print("Passing the trace function")

def profile():
    print("PROFILE THREAD: " + str(threading.current_thread().getName()))

class mythread(threading.Thread):
    def __init__(self, thread_name, thread_ID):
        threading.Thread.__init__(self)
        self.thread_name = thread_name
        self.thread_ID = thread_ID

    def run(self):
        print(str(self.thread_ID))
        print("ACTIVE THREADS ARE: " + str(threading.active_count()))
        print("CURRENT THREAD IS: " + str(threading.current_thread().getName()))

# メインスレッドの情報を表示
print("NAME OF THE MAIN THREAD: " + str(threading.main_thread().getName()))
print("IDENTIFICATION OF MAIN THREAD: " + str(threading.get_ident()))
print("STACK SIZE = " + str(threading.stack_size()))

# トレース関数とプロファイル関数を設定
threading.settrace(trace_function())
threading.setprofile(profile())

# スレッドを生成して起動
my_thread1 = mythread("PP", 500)
my_thread2 = mythread("PythonProgram", 1000)

my_thread1.start()
my_thread2.start()

print("LIST OF ENUMERATION: ")
print(threading.enumerate())
print("EXIT")

実行結果の例

NAME OF THE MAIN THREAD: MainThread
IDENTIFICATION OF MAIN THREAD: 5436
STACK SIZE = 0
Passing the trace function
None
PROFILE THREAD: MainThread
500
ACTIVE THREADS ARE: 3
CURRENT THREAD IS: Thread-1
1000
ACTIVE THREADS ARE: 2
CURRENT THREAD IS: Thread-2
LIST OF ENUMERATION: 
[<_MainThread(MainThread, started 5436)>, <mythread(Thread-1, started 8460)>, <mythread(Thread-2, started 4668)>]
EXIT

※スレッド識別子や表示順序は、実行環境やタイミングによって毎回変わります。複数のスレッドが並行して動作しているため、出力の順序も一定ではありません。

押さえておきたいポイント

  • GIL(グローバルインタプリタロック):CPythonではGILの仕組みにより、CPU負荷の高い処理(CPUバウンド)はスレッドを使っても真の同時実行にはなりません。そのため、スレッドはファイルI/Oやネットワーク通信などの待ち時間が発生する処理(I/Oバウンド)で特に効果を発揮します。
  • getName()の代替:Python 3.10以降では、getName()の代わりに.name属性への直接アクセスが推奨されています。
  • デーモンスレッド:setDaemon(True)またはコンストラクタ引数daemon=Trueを指定すると、メインスレッド終了時に自動的に終了するバックグラウンドスレッドを作成できます。

まとめ

threadingモジュールを使えば、スレッドの状態取得からトレース・プロファイリングまで、並列処理に必要な機能を高水準なAPIで扱えます。ただし、GILの影響を考慮し、I/O待ちが多い処理の並行化に活用するのが実践的な使い方です。CPUバウンドな処理を高速化したい場合は、multiprocessingモジュールによるプロセスベースの並列化も検討しましょう。

  1. PythonのTkinterでスレッドを使う方法|threadingモジュールによる非同期処理の基本

    Tkinterでは、スレッド(Threading)を活用することで、複数の関数を同時に実行できます。これにより、アプリケーション内の処理を非同期で動かすことが可能になります。 Pythonでスレッドを使用するには、まずthreadingモジュールをインポートし、そのThreadクラスを継承した新しいクラスを定義します。そして、クラス内でrunメソッドをオーバーライドし、実行したい処理をその中に記述します。 つまり、スレッドを使えば複数の作業を並行して進めることができます。Tkinterアプリケーションでスレッドを実現する際は、Thread()関数を利用します。 ここでは、「一定時間スリープした

  2. 【初心者向け】Pythonのissuperset()メソッドの使い方をわかりやすく解説

    はじめにこの記事では、Pythonのissuperset()メソッドについて、基本的な仕組みから実際のコード例まで詳しく解説します。issuperset()は、セット(集合)に対して使用できるメソッドで、引数として渡されたセットのすべての要素が、呼び出し元のセットに含まれているかどうかを判定します。呼び出し元のセットBが、引数のセットAのすべての要素を含んでいる場合 → True を返すセットAの要素がすべてBに含まれていない場合 → False を返すつまり、「BがAの上位集合(スーパーセット)であるかどうか」を判定するためのメソッドです。基本構文B.issuperset(A)この式は、Bが