0

Python(3.5)のコンテキストマネージャを使用しようとすると、次の2つのシナリオで動作が異なります。コンテキストマネージャと組み合わせて適切なシャットダウンプロシージャを使用することで、私のスレッドプログラムの例外を正常に処理しようとしていますが、2番目のケースで動作するようにはできません。 。一度start(内部runを呼び出すthreading.Thread親クラスによって提供される方法)で開始コンテキストクラスとコンテキストマネージャのメソッドデコレータをPythonで使用するときの動作が異なります

import threading 
class Job(threading.Thread): 
    def run(self): 
     self.active = True 

     while self.active: 
      continue 

    def stop(self): 
     self.active = False 

、それを停止することができます。両方のケースに共通する

はスレッド使用し、一般的な「仕事」作業ですstopに電話してください。私はそれが次のコードを使用して実行

class Context(object): 
    def __init__(self): 
     self.active = False 

    def __enter__(self): 
     print("Entering context") 
     self.job = Job() 
     self.job.start() 
     return self.job 

    def __exit__(self, exc_type, exc_value, traceback): 
     print("Exiting context") 
     self.job.stop() 
     self.job.join() 
     print("Job stopped") 

:Pythonのwith声明サポートを利用するように

私はこれを行うことを試みた最初の方法は、__enter__を内蔵し、__exit__メソッドを使用していた

with Context(): 
    while input() != "stop": 
     continue 

これは、ユーザーが「停止」と入力してEnterを押すまで待機します。このループ中にユーザーが代わりにKeyboardInterruptを作成するためにCtrl+Cを押すと、__exit__メソッドがまだ呼び出されます

from contextlib import contextmanager 

@contextmanager 
def job_context(): 
    print("Entering context") 
    job = Job() 
    job.start() 
    yield job 
    print("Exiting context") 
    job.stop() 
    job.join() 
    print("Job stopped") 
:私はこれを実行しようとしました

Entering context 
^CExiting context 
Job stopped 
Traceback (most recent call last): 
    File "tmp2.py", line 48, in <module> 
    while input() != "stop": 
KeyboardInterrupt 

第二の方法は@contextmanagerデコレータを使用して関数を作成することでした

私は再びwith文を使用して、それを実行します。

with job_context(): 
    while input() != "stop": 
     continue 

しかし、私は実行しますそしてCtrl+Cを押すと、最初の例の__exit__メソッドに相当するyieldのコードが実行されません。代わりに、Pythonスクリプトは無限ループで実行され続けます。

Entering context 
^CTraceback (most recent call last): 
    File "tmp2.py", line 42, in <module> 
    while input() != "stop": 
KeyboardInterrupt 
^CException ignored in: <module 'threading' from '/usr/lib/python3.5/threading.py'> 
Traceback (most recent call last): 
    File "/usr/lib/python3.5/threading.py", line 1288, in _shutdown 
    t.join() 
    File "/usr/lib/python3.5/threading.py", line 1054, in join 
    self._wait_for_tstate_lock() 
    File "/usr/lib/python3.5/threading.py", line 1070, in _wait_for_tstate_lock 
    elif lock.acquire(block, timeout): 
KeyboardInterrupt 

あなたは、私が割り込みを作成するためにCtrl+Cを押し^Cシンボルを見ることができます。プログラムを停止するには、私はCtrl+Cyield後のコードが実行されていないこの時点で二回目を押す必要があります。最初のケースで__exit__に相当するシャットダウンコードを実行しない2番目のケースでは何が違うのですか? the documentation当たり

答えて

1

未処理の例外は、ブロックで発生した場合、それは降伏が発生時点で ジェネレータ内部リレイズされています。したがって、 try ... except ... finallyステートメントを使用してエラーをトラップするか、または を使用してクリーンアップを確実に行うことができます。あなたのケースでは

、これは次のようになります。それをやったこと

@contextmanager 
def job_context(): 
    print("Entering context") 
    job = Job() 
    job.start() 
    try: 
     yield job 
    finally: 
     print("Exiting context") 
     job.stop() 
     job.join() 
     print("Job stopped") 
     raise 
+0

!ありがとうございました。 yieldコマンドを使用したメソッドで例外がスローされるのは、直感的ではありません。確かに、例外が生成された場所でスローされるべきですか? – Sean

+0

「生産された場所」*は何を意味しますか? – jonrsharpe

+0

'KeyboardException'はメインループで発生しますので、ジェネレータでリレイズされるのは奇妙です。 – Sean

関連する問題