親プロセスに例外を渡す方法がありますか?それから、あなたが望むだけでそれらを扱うことができます。
concurrent.futures.ProcessPoolExecutor
を使用すると、これは自動的です。 multiprocessing.Pool
を使用すると、それは簡単です。 Process
とQueue
を明示的に使用する場合は、作業を少しずつ行う必要がありますが、それはであり、それほど多くはありません。です。例えば
:
def run(self):
try:
for i in iter(self.inputQueue.get, 'STOP'):
# (code that does stuff)
1/0 # Dumb error
# (more code that does stuff)
self.outputQueue.put(result)
except Exception as e:
self.outputQueue.put(e)
次に、あなたの呼び出し元のコードは、ちょうど何か他のもののようなキューからException
Sを読み取ることができます。この代わりに:(私はあなたの最小限のサンプルだけでキューを無視しているため、実際の親プロセスキュー読み取りコードが何をするか知っている。しかし、できればしないでください
result = outq.pop()
if isinstance(result, Exception):
raise result
yield result
:。
yield outq.pop()
これを行います
これは、run
になる未処理の例外を中止することを前提としています。これは、実際のコードが実際にはこのように機能しない場合でも考えられます。例外を返信して次のi in iter
に進む場合は、try
をfor
の代わりに移動してください。
これはまた、Exception
が有効な値ではないことを前提としています。それが問題だ場合、最も簡単な解決策はただ(result, exception)
タプルプッシュすることです。そして、
def run(self):
try:
for i in iter(self.inputQueue.get, 'STOP'):
# (code that does stuff)
1/0 # Dumb error
# (more code that does stuff)
self.outputQueue.put((result, None))
except Exception as e:
self.outputQueue.put((None, e))
を、あなたのポッピングのコードがこれを行う:
result, exception = outq.pop()
if exception:
raise exception
yield result
あなたは、これはNode.jsのコールバックに似ていることに気づくことがありスタイルでは、すべてのコールバックに(err, result)
を渡します。はい、それは迷惑で、あなたはそのスタイルでコードを台無しにするつもりです。しかし、実際にはラッパー以外の場所では使用していません。キューから値を取得するか、またはrun
の内部で呼び出されるすべての "アプリケーションレベル"のコードは、通常の返品/利回りと発生した例外を表示します。
Future
をconcurrent.futures
という仕様(またはそのクラスをそのまま使用)にすることも、ジョブのキューイングを実行していても手動で実行しているとします。これは難しいことではありません。特に、デバッグ用の非常に優れたAPIを提供します。
最後に、キューごとに1つのワーカーだけが確実に必要な場合でも、エグゼキュータ/プールの設計では、ワーカーやキューを使用して構築されたほとんどのコードを非常に簡単にすることができます。すべてのボイラープレートを取り除き、Worker.run
メソッドのループを関数に置き換えます(キューに追加するのではなく、通常はreturn
sまたはraise
sです)。呼び出し側では、すべてのボイラープレートを再度スクラップし、そのパラメータを使用してジョブ機能をsubmit
またはmap
とするだけです。
あなたの全体の例では、に削減することができます。
def job(i):
# (code that does stuff)
1/0 # Dumb error
# (more code that does stuff)
return result
with concurrent.futures.ProcessPoolExecutor(max_workers=1) as executor:
results = executor.map(job, range(10))
そして、それは自動的に適切に例外を処理します。
コメントに記載されているとおり、例外のトレースバックは子プロセスをトレースしません。手動のraise result
コール(または、プールまたはエグゼキュータを使用している場合は、プールまたはエグゼキュータの不具合)までしか流れません。
multiprocessing.Queue
はpickle
の上に構築され、酸洗例外はそのトレースバックを漬けません。そしてその理由は、あなたがトレースバックをピケることができないということです。その理由は、トレースバックにはローカル実行コンテキストへの参照がたくさんあるため、別のプロセスで動作させることは非常に難しいことです。
だから、これについて何ができますか?完全に一般的な解決法を探しに行ってはいけません。代わりに、実際に必要なものについて考えてみてください。 90%の時間、 "トレースバックで例外をログに記録し、続行する"または "トレースバックで例外をデフォルトの未処理例外ハンドラのようにstderr
とexit(1)
に出力する"いずれの場合でも、例外をまったく渡す必要はありません。子側でフォーマットし、文字列を渡します。 の場合、には何かもっと欲しいものが必要です。必要なものを正確に整理し、手動でまとめておくだけの十分な情報を渡します。トレースバックと例外をフォーマットする方法がわからない場合は、traceback
モジュールを参照してください。それはかなり簡単です。そして、これはあなたがピクルの機械にまったく入る必要がないことを意味します。 (copyreg
ピックラーや、__reduce__
メソッドなどのホルダークラスを書くことは非常に難しいことではありませんが、そうしなければならない理由は何ですか?)
プロセスの例外を示すため、このような回避策を示唆? – Blender
@ブレンダーいくつかのコードを追加しました。 – hendra