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

スレッド内で発生したPython例外を処理する方法


Pythonでは、スレッド内で発生した例外は自動的にメインスレッドへ伝播しません。そのため、ワーカースレッドの中でエラーが起こっても、メインプログラム側はそれに気づくことができません。

この問題を解決するのがQueue(キュー)を使った手法です。スレッド内で例外を捕捉した際に、sys.exc_info()で取得した例外情報をキューに格納し、メインスレッドが後からそのキューを読み取ることで、スレッド内の例外を確実に処理できます。

スレッド例外をQueueで受け渡すサンプルコード

以下のコードでは、スレッド内で発生した例外を捕捉し、キュー経由でメインスレッドに通知しています。

import sys
import threading
import time
import Queue

def thread(args1, stop_event, queue_obj):
    print "start thread"
    stop_event.wait(12)
    if not stop_event.is_set():
        try:
            raise Exception("boom!")
        except Exception:
            queue_obj.put(sys.exc_info())

try:
    queue_obj = Queue.Queue()
    t_stop = threading.Event()
    t = threading.Thread(target=thread, args=(1, t_stop, queue_obj))
    t.start()
    time.sleep(15)
    print "stop thread!"
    t_stop.set()
    try:
        exc = queue_obj.get(block=False)
    except Queue.Empty:
        pass
    else:
        exc_type, exc_obj, exc_trace = exc
        print exc_obj
except Exception as e:
    print "It took too long"

実行結果

C:/Users/TutorialsPoint1/~.py
start thread
stop thread!
boom!

コードのポイント解説

1. スレッド関数内での例外捕捉
thread()関数の中でraise Exception("boom!")により意図的に例外を発生させ、exceptブロックで捕捉しています。捕捉した例外はsys.exc_info()によって「例外クラス・例外オブジェクト・トレースバック」のタプルとして取得され、queue_obj.put()でキューに格納されます。

2. Eventによるスレッドの停止制御
threading.Event()で生成したt_stopイベントを使用し、stop_event.wait(12)で最大12秒間待機します。メインスレッドが15秒後にt_stop.set()を呼び出すとイベントがセットされ、is_set()がTrueを返すため、それ以降は例外処理へ進みません。

3. メインスレッドでの例外受け取り
メインスレッドはqueue_obj.get(block=False)でキューから例外情報を非ブロッキングで取得します。キューが空の場合はQueue.Empty例外が発生するため、except節で無視しています。例外情報が取得できた場合はタプルを展開し、exc_obj(例外オブジェクト)を出力することで「boom!」と表示されます。

補足: 上記のコードはPython 2系の記法(print文やQueueモジュール)で書かれています。Python 3で動作させる場合は、import queueと変更し、print()関数形式に書き換える必要があります。

  1. JavaのUncaughtExceptionHandlerでキャッチされない例外を処理する方法

    UncaughtExceptionHandlerとはUncaughtExceptionHandlerは、Threadクラス内に定義されたインターフェースです。メインスレッドがキャッチされない例外(未処理の例外)によって終了しようとするとき、JVM(Java仮想マシン)はそのスレッドに設定されたUncaughtExceptionHandlerを呼び出します。これにより、スレッドが強制終了される前に、例外情報をファイルにログとして記録する、あるいはログをサーバーへアップロードするといったエラーハンドリングを行うことが可能になります。また、すべての未処理の例外に対して共通して呼び出されるデフォルト例

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

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