Pythonの呼び出し側スレッドでスレッドの例外をキャッチする


208

私はPythonとマルチスレッドプログラミング全般に非常に慣れていません。基本的に、ファイルを別の場所にコピーするスクリプトがあります。これを別のスレッドに配置し....て、出力がスクリプトがまだ実行中であることを示すことができるようにしたいと思います。

私が抱えている問題は、ファイルをコピーできない場合に例外がスローされることです。これは、メインスレッドで実行している場合は問題ありません。ただし、次のコードを使用しても機能しません。

try:
    threadClass = TheThread(param1, param2, etc.)
    threadClass.start()   ##### **Exception takes place here**
except:
    print "Caught an exception"

スレッドクラス自体で、例外を再スローしようとしましたが、機能しません。ここにいる人々が同様の質問をするのを見てきましたが、彼らはすべて私がやろうとしていることよりも具体的なことをしているようです(そして私は提供されるソリューションを完全に理解していません)。私は人々がの使用法について言及するのを見ましたsys.exc_info()が、それをどこでどのように使用するかわかりません。

すべての助けに大歓迎です!

編集:スレッドクラスのコードは以下のとおりです。

class TheThread(threading.Thread):
    def __init__(self, sourceFolder, destFolder):
        threading.Thread.__init__(self)
        self.sourceFolder = sourceFolder
        self.destFolder = destFolder

    def run(self):
        try:
           shul.copytree(self.sourceFolder, self.destFolder)
        except:
           raise

内部で何が起こっているかについて、より深い洞察を提供できますTheThreadか?コードサンプルでしょうか?
ジェサニズム

承知しました。上記の回答を編集して、いくつかの詳細を含めます。
ファント

1
メインスレッドが何かを行うビットであり、進行状況インジケーターがスポーンされたスレッド内にあるように、ラウンドを切り替えることを検討しましたか?
ダンヘッド

1
ダンヘッド、最初に「...」関数を生成し、次にコピー関数を実行するメインスレッドを参照していますか?それは機能し、例外の問題を回避できます。しかし、私はまだpythonで適切にスレッド化する方法を学びたいです。
ファント

回答:


114

問題は、thread_obj.start()すぐに戻ることです。作成した子スレッドは、独自のスタックを使用して、独自のコンテキストで実行されます。そこで発生する例外は、子スレッドのコンテキスト内にあり、それ自体のスタック内にあります。この情報を親スレッドに伝達するために今考えることができる1つの方法は、何らかのメッセージパッシングを使用することです。

サイズを試してみてください:

import sys
import threading
import Queue


class ExcThread(threading.Thread):

    def __init__(self, bucket):
        threading.Thread.__init__(self)
        self.bucket = bucket

    def run(self):
        try:
            raise Exception('An error occured here.')
        except Exception:
            self.bucket.put(sys.exc_info())


def main():
    bucket = Queue.Queue()
    thread_obj = ExcThread(bucket)
    thread_obj.start()

    while True:
        try:
            exc = bucket.get(block=False)
        except Queue.Empty:
            pass
        else:
            exc_type, exc_obj, exc_trace = exc
            # deal with the exception
            print exc_type, exc_obj
            print exc_trace

        thread_obj.join(0.1)
        if thread_obj.isAlive():
            continue
        else:
            break


if __name__ == '__main__':
    main()

5
この醜いwhileループの代わりにスレッドに参加してみませんか?multiprocessing同等のものを参照してください:gist.github.com/2311116
schlamar

1
@Lasseの回答に基づいて、EventHookパターンstackoverflow.com/questions/1092531/event-system-in-python/…を使用しないのはなぜですか?ループというよりは?
Andre Miras、2014年

1
エラーの完全なキューが必要な場合を除き、キューはエラーを通知するのに最適な手段ではありません。より良い構成はthreading.Event()です
Muposat '19年

1
これは私には安全ではないようです。スレッドが発生した直後に例外を発生さbucket.get()せるとQueue.Emptyどうなりますか?その後、スレッドはとjoin(0.1)を完了しisAlive() is False、例外を逃します。
Steve

1
Queueこの単純なケースでは不要です-が例外の直後に完了ExcThreadすることを確認する限り、例外情報をのプロパティとして保存できrun()ます(この単純な例ではそれが行われます)。その後、単に(またはその間に)例外を再発生させt.join()ます。join()スレッドが完了したことを確認するため、同期の問題はありません。stackoverflow.com/a/12223550/126362の
ejm

42

このconcurrent.futuresモジュールにより、個別のスレッド(またはプロセス)での作業が簡単になり、結果として生じる例外を処理できます。

import concurrent.futures
import shutil

def copytree_with_dots(src_path, dst_path):
    with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor:
        # Execute the copy on a separate thread,
        # creating a future object to track progress.
        future = executor.submit(shutil.copytree, src_path, dst_path)

        while future.running():
            # Print pretty dots here.
            pass

        # Return the value returned by shutil.copytree(), None.
        # Raise any exceptions raised during the copy process.
        return future.result()

concurrent.futuresはPython 3.2に含まれており、以前のバージョンのバックポートfuturesされモジュールとして使用できます。


5
これは、OPが要求したことを正確に行うわけではありませんが、まさに私が必要としたヒントです。ありがとうございました。
Mad Physicist

2
そして、でconcurrent.futures.as_completed、例外が発生しているとして、あなたはすぐに通知を受けることができます。stackoverflow.com/questions/2829329/...
チロSantilli郝海东冠状病六四事件法轮功

1
このコードはメインスレッドをブロックしています。これを非同期にするにはどうすればよいですか?
Nikolay Shindarov

40

この質問には本当に奇妙で複雑な答えがたくさんあります。これは私にとってほとんどのことで十分に思えるので、私はこれを過度に単純化していますか

from threading import Thread

class PropagatingThread(Thread):
    def run(self):
        self.exc = None
        try:
            if hasattr(self, '_Thread__target'):
                # Thread uses name mangling prior to Python 3.
                self.ret = self._Thread__target(*self._Thread__args, **self._Thread__kwargs)
            else:
                self.ret = self._target(*self._args, **self._kwargs)
        except BaseException as e:
            self.exc = e

    def join(self):
        super(PropagatingThread, self).join()
        if self.exc:
            raise self.exc
        return self.ret

Pythonのいずれかのバージョンでのみ実行することが確実である場合は、run()メソッドをマングルバージョンのみに減らすことができます(3より前のバージョンのPythonでのみ実行する場合)。または、クリーンバージョンのみ(3で始まるバージョンのPythonでのみ実行する場合)。

使用例:

def f(*args, **kwargs):
    print(args)
    print(kwargs)
    raise Exception('I suck at this')

t = PropagatingThread(target=f, args=(5,), kwargs={'hello':'world'})
t.start()
t.join()

参加すると、他のスレッドで発生した例外が表示されます。

sixまたはPython 3のみを使用している場合は、例外が再発生したときに取得するスタックトレース情報を改善できます。結合の時点でスタックだけではなく、内部例外を新しい外部例外にラップして、両方のスタックトレースを取得できます。

six.raise_from(RuntimeError('Exception in thread'),self.exc)

または

raise RuntimeError('Exception in thread') from self.exc

1
この答えがあまり人気がない理由はわかりません。単純な伝播を行うものもありますが、クラスの拡張とオーバーライドが必要です。これは、多くの人が期待することを実行するだけで、ThreadからProagatingThreadに変更するだけで済みます。そして、4つのスペースタブなので、コピー/貼り付けは簡​​単でした:-) ...私が提案する唯一の改善は、six.raise_from()を使用することです。これにより、リレイズのサイト。
aggieNick02

どうもありがとうございました。非常にシンプルなソリューション。
sonulohani

私の問題は、複数の子スレッドがあることです。結合は順番に実行され、後で結合されたスレッドから例外が発生する可能性があります。私の問題に対する簡単な解決策はありますか?結合を同時に実行しますか?
チュアン

ありがとう、それは完璧に機能します!なぜpythonで直接処理されないのかわからない…
GG。

これは最も有用な答えの定義です。このソリューションは他のソリューションよりもはるかに一般的でありながら、単純です。プロジェクトで使用します!
コンスタンティンSekeresh

30

別のスレッドでスローされた例外を直接キャッチすることはできませんが、この機能に非常に近いものを非常に透過的に取得するコードを次に示します。子スレッドは、ExThreadクラスの代わりにクラスをサブクラス化する必要がthreading.Threadあり、親スレッドは、スレッドがジョブを完了するのを待つときではchild_thread.join_with_exception()なく、メソッドを呼び出す必要がありますchild_thread.join()

この実装の技術的な詳細:子スレッドが例外をスローすると、それはを介して親に渡さQueueれ、親スレッドで再度スローされます。このアプローチでは、忙しい待ち時間がないことに注意してください。

#!/usr/bin/env python

import sys
import threading
import Queue

class ExThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.__status_queue = Queue.Queue()

    def run_with_exception(self):
        """This method should be overriden."""
        raise NotImplementedError

    def run(self):
        """This method should NOT be overriden."""
        try:
            self.run_with_exception()
        except BaseException:
            self.__status_queue.put(sys.exc_info())
        self.__status_queue.put(None)

    def wait_for_exc_info(self):
        return self.__status_queue.get()

    def join_with_exception(self):
        ex_info = self.wait_for_exc_info()
        if ex_info is None:
            return
        else:
            raise ex_info[1]

class MyException(Exception):
    pass

class MyThread(ExThread):
    def __init__(self):
        ExThread.__init__(self)

    def run_with_exception(self):
        thread_name = threading.current_thread().name
        raise MyException("An error in thread '{}'.".format(thread_name))

def main():
    t = MyThread()
    t.start()
    try:
        t.join_with_exception()
    except MyException as ex:
        thread_name = threading.current_thread().name
        print "Caught a MyException in thread '{}': {}".format(thread_name, ex)

if __name__ == '__main__':
    main()

1
捕まえたくBaseExceptionないExceptionですか?あなたがしていることは、例外をあるものThreadから別の中に伝播することです。現在、IE、KeyboardInterruptはバックグラウンドスレッドで発生した場合、黙って無視されます。
ArtOfWarfare 2015

join_with_exceptionデッドスレッドで2回目に呼び出されると、無期限にハングします。修正:github.com/fraserharris/threading-extensions/blob/master/…
フレーザー・ハリス

Queue必要ないと思います。@Santaの回答に対する私のコメントを参照してください。あなたはそれをstackoverflow.com/a/12223550/126362の
ejm

22

スレッドで例外が発生した場合、最善の方法は、中に呼び出し元のスレッドで例外を再発生させることjoinです。sys.exc_info()関数を使用して、現在処理されている例外に関する情報を取得できます。この情報joinは、呼び出されるまでスレッドオブジェクトのプロパティとして単純に保存でき、その時点で再生成できます。

Queue.Queue(他の回答で提案されているように)スレッドが多くても1つの例外スローし、例外をスローした直後に完了するというこの単純なケースでは、a は必要ないことに注意してください。スレッドが完了するのを待つだけで、競合状態を回避します。

たとえば、拡張ExcThread(下)、上書きexcRun(の代わりrun)。

Python 2.x:

import threading

class ExcThread(threading.Thread):
  def excRun(self):
    pass

  def run(self):
    self.exc = None
    try:
      # Possibly throws an exception
      self.excRun()
    except:
      import sys
      self.exc = sys.exc_info()
      # Save details of the exception thrown but don't rethrow,
      # just complete the function

  def join(self):
    threading.Thread.join(self)
    if self.exc:
      msg = "Thread '%s' threw an exception: %s" % (self.getName(), self.exc[1])
      new_exc = Exception(msg)
      raise new_exc.__class__, new_exc, self.exc[2]

Python 3.x:

の3つの引数形式 raiseはPython 3でなくなったので、最後の行を次のように変更します。

raise new_exc.with_traceback(self.exc[2])

2
なぜsuper(ExcThread、self).join()の代わりにthreading.Thread.join(self)を使用するのですか?
RichardMöhn18年

9

concurrent.futures.as_completed

https://docs.python.org/3.7/library/concurrent.futures.html#concurrent.futures.as_completed

次のソリューション:

  • 例外が呼び出されるとすぐにメインスレッドに戻ります
  • 必要がないため、追加のユーザー定義クラスは必要ありません。
    • 明示的な Queue
    • あなたの作業スレッドの周りにexcept elseを追加する

ソース:

#!/usr/bin/env python3

import concurrent.futures
import time

def func_that_raises(do_raise):
    for i in range(3):
        print(i)
        time.sleep(0.1)
    if do_raise:
        raise Exception()
    for i in range(3):
        print(i)
        time.sleep(0.1)

with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor:
    futures = []
    futures.append(executor.submit(func_that_raises, False))
    futures.append(executor.submit(func_that_raises, True))
    for future in concurrent.futures.as_completed(futures):
        print(repr(future.exception()))

可能な出力:

0
0
1
1
2
2
0
Exception()
1
2
None

残念ながら、フューチャーを倒して他のフューチャーをキャンセルすることはできません。

次のようなことをした場合:

for future in concurrent.futures.as_completed(futures):
    if future.exception() is not None:
        raise future.exception()

次に、withそれをキャッチし、2番目のスレッドが完了するのを待ってから続行します。以下は同様に動作します。

for future in concurrent.futures.as_completed(futures):
    future.result()

以来、future.result()1が発生した場合に例外を再発生させます。

Pythonプロセス全体を終了したい場合は、 os._exit(0)は、で済むが、これはおそらくリファクタリングが必要であることを意味します。

完全な例外セマンティクスを持つカスタムクラス

私は自分で完璧なインターフェースをコーディングすることになりました:同時に実行するスレッドの最大数を制限する正しい方法は?セクション「エラー処理を伴うキューの例」。このクラスは、便利であり、送信と結果/エラー処理を完全に制御できるようにすることを目的としています。

Python 3.6.7、Ubuntu 18.04でテスト済み。


4

これは厄介な小さな問題でした。私の解決策を紹介します。他の解決策(async.ioなど)は有望に見えましたが、ブラックボックスも表示されていました。キュー/イベントループアプローチは、ある種の実装に結びつけます。ただし、コンカレントフューチャーのソースコードは約1000行であり、簡単に理解できます。ます。それは私が私の問題を簡単に解決することを可能にしました:あまりセットアップせずにアドホックワーカースレッドを作成し、メインスレッドで例外をキャッチできるようにしました。

私のソリューションでは、コンカレントFutures APIとスレッドAPIを使用しています。スレッドと未来の両方を提供するワーカーを作成できます。そうすれば、スレッドに参加して結果を待つことができます。

worker = Worker(test)
thread = worker.start()
thread.join()
print(worker.future.result())

...または、完了時にワーカーにコールバックを送信させることができます。

worker = Worker(test)
thread = worker.start(lambda x: print('callback', x))

...またはイベントが完了するまでループすることができます:

worker = Worker(test)
thread = worker.start()

while True:
    print("waiting")
    if worker.future.done():
        exc = worker.future.exception()
        print('exception?', exc)
        result = worker.future.result()
        print('result', result)           
        break
    time.sleep(0.25)

これがコードです:

from concurrent.futures import Future
import threading
import time

class Worker(object):
    def __init__(self, fn, args=()):
        self.future = Future()
        self._fn = fn
        self._args = args

    def start(self, cb=None):
        self._cb = cb
        self.future.set_running_or_notify_cancel()
        thread = threading.Thread(target=self.run, args=())
        thread.daemon = True #this will continue thread execution after the main thread runs out of code - you can still ctrl + c or kill the process
        thread.start()
        return thread

    def run(self):
        try:
            self.future.set_result(self._fn(*self._args))
        except BaseException as e:
            self.future.set_exception(e)

        if(self._cb):
            self._cb(self.future.result())

...そしてテスト関数:

def test(*args):
    print('args are', args)
    time.sleep(2)
    raise Exception('foo')

2

スレッディングの初心者として、Mateusz Kobosのコード(上記)の実装方法を理解するのに長い時間がかかりました。これは、それを使用する方法を理解するのに役立つ明確なバージョンです。

#!/usr/bin/env python

import sys
import threading
import Queue

class ExThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.__status_queue = Queue.Queue()

    def run_with_exception(self):
        """This method should be overriden."""
        raise NotImplementedError

    def run(self):
        """This method should NOT be overriden."""
        try:
            self.run_with_exception()
        except Exception:
            self.__status_queue.put(sys.exc_info())
        self.__status_queue.put(None)

    def wait_for_exc_info(self):
        return self.__status_queue.get()

    def join_with_exception(self):
        ex_info = self.wait_for_exc_info()
        if ex_info is None:
            return
        else:
            raise ex_info[1]

class MyException(Exception):
    pass

class MyThread(ExThread):
    def __init__(self):
        ExThread.__init__(self)

    # This overrides the "run_with_exception" from class "ExThread"
    # Note, this is where the actual thread to be run lives. The thread
    # to be run could also call a method or be passed in as an object
    def run_with_exception(self):
        # Code will function until the int
        print "sleeping 5 seconds"
        import time
        for i in 1, 2, 3, 4, 5:
            print i
            time.sleep(1) 
        # Thread should break here
        int("str")
# I'm honestly not sure why these appear here? So, I removed them. 
# Perhaps Mateusz can clarify?        
#         thread_name = threading.current_thread().name
#         raise MyException("An error in thread '{}'.".format(thread_name))

if __name__ == '__main__':
    # The code lives in MyThread in this example. So creating the MyThread 
    # object set the code to be run (but does not start it yet)
    t = MyThread()
    # This actually starts the thread
    t.start()
    print
    print ("Notice 't.start()' is considered to have completed, although" 
           " the countdown continues in its new thread. So you code "
           "can tinue into new processing.")
    # Now that the thread is running, the join allows for monitoring of it
    try:
        t.join_with_exception()
    # should be able to be replace "Exception" with specific error (untested)
    except Exception, e: 
        print
        print "Exceptioon was caught and control passed back to the main thread"
        print "Do some handling here...or raise a custom exception "
        thread_name = threading.current_thread().name
        e = ("Caught a MyException in thread: '" + 
             str(thread_name) + 
             "' [" + str(e) + "]")
        raise Exception(e) # Or custom class of exception, such as MyException

2

RickardSjogrenのQueueやsysなどのない方法と似ていますが、シグナルに対するリスナーもありません。exceptブロックに対応する例外ハンドラーを直接実行します。

#!/usr/bin/env python3

import threading

class ExceptionThread(threading.Thread):

    def __init__(self, callback=None, *args, **kwargs):
        """
        Redirect exceptions of thread to an exception handler.

        :param callback: function to handle occured exception
        :type callback: function(thread, exception)
        :param args: arguments for threading.Thread()
        :type args: tuple
        :param kwargs: keyword arguments for threading.Thread()
        :type kwargs: dict
        """
        self._callback = callback
        super().__init__(*args, **kwargs)

    def run(self):
        try:
            if self._target:
                self._target(*self._args, **self._kwargs)
        except BaseException as e:
            if self._callback is None:
                raise e
            else:
                self._callback(self, e)
        finally:
            # Avoid a refcycle if the thread is running a function with
            # an argument that has a member that points to the thread.
            del self._target, self._args, self._kwargs, self._callback

self._callbackとrun()のexceptブロックのみが通常のthreading.Threadに追加されます。


2

私はここでパーティーに少し遅れていることを知っていますが、非常に類似した問題がありましたが、GUIとしてtkinterを使用することが含まれており、メインループにより.join()に依存するソリューションを使用できなくなりました。そのため、元の質問の編集で与えられた解決策を採用しましたが、他の人が理解しやすいように、より一般的にしました。

実際の新しいスレッドクラスは次のとおりです。

import threading
import traceback
import logging


class ExceptionThread(threading.Thread):
    def __init__(self, *args, **kwargs):
        threading.Thread.__init__(self, *args, **kwargs)

    def run(self):
        try:
            if self._target:
                self._target(*self._args, **self._kwargs)
        except Exception:
            logging.error(traceback.format_exc())


def test_function_1(input):
    raise IndexError(input)


if __name__ == "__main__":
    input = 'useful'

    t1 = ExceptionThread(target=test_function_1, args=[input])
    t1.start()

もちろん、ログを出力したり、コンソールに出力したりなど、他の方法で例外を処理することもできます。

これにより、特別な変更を加えることなく、Threadクラスとまったく同じようにExceptionThreadクラスを使用できます。


1

私が気に入っている方法の1つは、オブザーバーパターンに基づいています。スレッドがリスナーに例外を発行するために使用するシグナルクラスを定義します。スレッドから値を返すためにも使用できます。例:

import threading

class Signal:
    def __init__(self):
        self._subscribers = list()

    def emit(self, *args, **kwargs):
        for func in self._subscribers:
            func(*args, **kwargs)

    def connect(self, func):
        self._subscribers.append(func)

    def disconnect(self, func):
        try:
            self._subscribers.remove(func)
        except ValueError:
            raise ValueError('Function {0} not removed from {1}'.format(func, self))


class WorkerThread(threading.Thread):

    def __init__(self, *args, **kwargs):
        super(WorkerThread, self).__init__(*args, **kwargs)
        self.Exception = Signal()
        self.Result = Signal()

    def run(self):
        if self._Thread__target is not None:
            try:
                self._return_value = self._Thread__target(*self._Thread__args, **self._Thread__kwargs)
            except Exception as e:
                self.Exception.emit(e)
            else:
                self.Result.emit(self._return_value)

if __name__ == '__main__':
    import time

    def handle_exception(exc):
        print exc.message

    def handle_result(res):
        print res

    def a():
        time.sleep(1)
        raise IOError('a failed')

    def b():
        time.sleep(2)
        return 'b returns'

    t = WorkerThread(target=a)
    t2 = WorkerThread(target=b)
    t.Exception.connect(handle_exception)
    t2.Result.connect(handle_result)
    t.start()
    t2.start()

    print 'Threads started'

    t.join()
    t2.join()
    print 'Done'

これは完全に安全な方法であると主張するために、スレッドを操作する十分な経験がありません。しかし、それは私のために働いており、私は柔軟性が好きです。


join()の後に切断しますか?
ealeon

私はそうではありませんが、それはあなたがぶらぶらしている未使用のものへの参照がないように、それは良い考えだと思います。
RickardSjogren、2015年

「handle_exception」がまだ子スレッドの一部であることに気づきました。スレッドの呼び出し元に渡す方法が必要
ealeon

1

裸の例外を使用することは、通常は取引よりも多くをキャッチするため、良い習慣ではありません。

except処理したい例外のみをキャッチするようにを変更することをお勧めします。TheThread外側にインスタンス化しようとすると、try例外が発生すると割り当てが発生しないため、これを発生させても望ましい効果は得られないと思います。

代わりに、次のように警告を出して次に進むことができます。

def run(self):
    try:
       shul.copytree(self.sourceFolder, self.destFolder)
    except OSError, err:
       print err

その後、その例外がキャッチされたら、そこで処理できます。次に、外部tryがからの例外をキャッチするTheThreadと、それがすでに処理した例外ではないことがわかり、プロセスフローを分離するのに役立ちます。


1
まあ、そのスレッドでエラーが発生した場合は、プログラム全体で問題が発生したことをユーザーに通知し、正常に終了する必要があります。そのため、メインスレッドですべての例外をキャッチして処理する必要があります。ただし、TheThreadが例外をスローした場合、メインスレッドのtry / exceptがまだそれをキャッチしないという問題が依然として存在します。スレッドに例外を検出させ、操作が失敗したことを示すfalseを返すようにすることができます。それでも同じ結果が得られますが、サブスレッドの例外を適切にキャッチする方法を知りたいです。
ファント

1

スレッドの例外をキャッチし、呼び出し元のメソッドに通信する簡単な方法は、辞書またはリストをworkerメソッドに渡すことです。

例(辞書をワーカーメソッドに渡す):

import threading

def my_method(throw_me):
    raise Exception(throw_me)

def worker(shared_obj, *args, **kwargs):
    try:
        shared_obj['target'](*args, **kwargs)
    except Exception as err:
        shared_obj['err'] = err

shared_obj = {'err':'', 'target': my_method}
throw_me = "Test"

th = threading.Thread(target=worker, args=(shared_obj, throw_me), kwargs={})
th.start()
th.join()

if shared_obj['err']:
    print(">>%s" % shared_obj['err'])

1

例外ストレージでスレッドをラップします。

import threading
import sys
class ExcThread(threading.Thread):

    def __init__(self, target, args = None):
        self.args = args if args else []
        self.target = target
        self.exc = None
        threading.Thread.__init__(self)

    def run(self):
        try:
            self.target(*self.args)
            raise Exception('An error occured here.')
        except Exception:
            self.exc=sys.exc_info()

def main():
    def hello(name):
        print(!"Hello, {name}!")
    thread_obj = ExcThread(target=hello, args=("Jack"))
    thread_obj.start()

    thread_obj.join()
    exc = thread_obj.exc
    if exc:
        exc_type, exc_obj, exc_trace = exc
        print(exc_type, ':',exc_obj, ":", exc_trace)

main()

0

pygolangsync.WorkGroupを提供しますこれは特に、発生したワーカースレッドからメインスレッドに例外を伝播します。例えば:

#!/usr/bin/env python
"""This program demostrates how with sync.WorkGroup an exception raised in
spawned thread is propagated into main thread which spawned the worker."""

from __future__ import print_function
from golang import sync, context

def T1(ctx, *argv):
    print('T1: run ... %r' % (argv,))
    raise RuntimeError('T1: problem')

def T2(ctx):
    print('T2: ran ok')

def main():
    wg = sync.WorkGroup(context.background())
    wg.go(T1, [1,2,3])
    wg.go(T2)

    try:
        wg.wait()
    except Exception as e:
        print('Tmain: caught exception: %r\n' %e)
        # reraising to see full traceback
        raise

if __name__ == '__main__':
    main()

実行すると次のようになります。

T1: run ... ([1, 2, 3],)
T2: ran ok
Tmain: caught exception: RuntimeError('T1: problem',)

Traceback (most recent call last):
  File "./x.py", line 28, in <module>
    main()
  File "./x.py", line 21, in main
    wg.wait()
  File "golang/_sync.pyx", line 198, in golang._sync.PyWorkGroup.wait
    pyerr_reraise(pyerr)
  File "golang/_sync.pyx", line 178, in golang._sync.PyWorkGroup.go.pyrunf
    f(pywg._pyctx, *argv, **kw)
  File "./x.py", line 10, in T1
    raise RuntimeError('T1: problem')
RuntimeError: T1: problem

質問の元のコードは次のとおりです。

    wg = sync.WorkGroup(context.background())

    def _(ctx):
        shul.copytree(sourceFolder, destFolder)
    wg.go(_)

    # waits for spawned worker to complete and, on error, reraises
    # its exception on the main thread.
    wg.wait()
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.