フラグ/セマフォなどを設定/チェックせずに実行中のスレッドを終了させることは可能ですか?
Pythonや他の言語では、一般的にスレッドを突然終了させるのは悪いパターンです。次のような場合を考えてください。
余裕がある場合(これが自分のスレッドを管理している場合)にこれを処理するための良い方法は、終了する時間かどうかを確認するために各スレッドが定期的にチェックするexit_requestフラグを持つことです。
例えば、次のとおりです。
import threading
class StoppableThread(threading.Thread):
"""Thread class with a stop() method. The thread itself has to check
regularly for the stopped() condition."""
def __init__(self):
super(StoppableThread, self).__init__()
self._stop_event = threading.Event()
def stop(self):
self._stop_event.set()
def stopped(self):
return self._stop_event.is_set()
このコードでは、スレッドを終了させたいときにスレッドでstop()を呼び出し、join()を使用してスレッドが正しく終了するのを待ちます。スレッドは定期的に停止フラグをチェックする必要があります。
ただし、本当にスレッドを強制終了する必要がある場合もあります。例としては、長時間の通話で忙しい外部ライブラリをラップしていて、それを中断したい場合があります。
次のコードでは、Pythonスレッドで例外を発生させることができます(制限付き)。
def _async_raise(tid, exctype):
'''Raises an exception in the threads with id tid'''
if not inspect.isclass(exctype):
raise TypeError("Only types can be raised (not instances)")
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid),
ctypes.py_object(exctype))
if res == 0:
raise ValueError("invalid thread id")
Elif res != 1:
# "if it returns a number greater than one, you're in trouble,
# and you should call it again with exc=NULL to revert the effect"
ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid), None)
raise SystemError("PyThreadState_SetAsyncExc failed")
class ThreadWithExc(threading.Thread):
'''A thread class that supports raising exception in the thread from
another thread.
'''
def _get_my_tid(self):
"""determines this (self's) thread id
CAREFUL : this function is executed in the context of the caller
thread, to get the identity of the thread represented by this
instance.
"""
if not self.isAlive():
raise threading.ThreadError("the thread is not active")
# do we have it cached?
if hasattr(self, "_thread_id"):
return self._thread_id
# no, look for it in the _active dict
for tid, tobj in threading._active.items():
if tobj is self:
self._thread_id = tid
return tid
# TODO: in python 2.6, there's a simpler way to do : self.ident
raise AssertionError("could not determine the thread's id")
def raiseExc(self, exctype):
"""Raises the given exception type in the context of this thread.
If the thread is busy in a system call (time.sleep(),
socket.accept(), ...), the exception is simply ignored.
If you are sure that your exception should terminate the thread,
one way to ensure that it works is:
t = ThreadWithExc( ... )
...
t.raiseExc( SomeException )
while t.isAlive():
time.sleep( 0.1 )
t.raiseExc( SomeException )
If the exception is to be caught by the thread, you need a way to
check that your thread has caught it.
CAREFUL : this function is executed in the context of the
caller thread, to raise an excpetion in the context of the
thread represented by this instance.
"""
_async_raise( self._get_my_tid(), exctype )
(Tomer Filibaによる Killable Threads に基づく。PyThreadState_SetAsyncExc
の戻り値についての引用は、 古いバージョンのPython からのものと思われます。)
ドキュメントに記載されているように、スレッドがPythonインタプリタの外部でビジー状態になっている場合は中断をキャッチしないため、これは魔法の弾丸ではありません。
このコードのよい使用パターンは、スレッドに特定の例外をキャッチさせてクリーンアップを実行させることです。そうすれば、タスクを中断しても適切なクリーンアップを実行できます。
それをするための公式のAPIはありません。
スレッドを強制終了するにはプラットフォームAPIを使用する必要があります。 pthread_kill、またはTerminateThread。あなたはそのようなAPIにアクセスすることができます。 pythonwinを通して、またはctypesを通して。
これは本質的に危険です。それはおそらく(ゴミになるスタックフレームのローカル変数から)収集不可能なゴミを招くでしょうし、殺されたスレッドが殺された時点でGILを持っていると、デッドロックを招くかもしれません。
multiprocessing.Process
はp.terminate()
できます
私がスレッドを強制終了したいが、フラグ/ロック/シグナル/セマフォ/イベント/その他を使用したくない場合は、スレッドを本格的なプロセスに昇格させます。ほんの数スレッドを使用するコードの場合、オーバーヘッドはそれほど悪くありません。
例えば。ブロッキングI/Oを実行するヘルパー「スレッド」を簡単に終了させるのに便利です。
変換は簡単です。関連するコードで、すべてのthreading.Thread
をmultiprocessing.Process
に、すべてのqueue.Queue
をmultiprocessing.Queue
に置き換え、必要なp.terminate()
の呼び出しを親プロセスに追加して、その子プロセスをp
プログラム全体を終了させようとしているのなら、スレッドを「デーモン」として設定することができます。 Thread.daemon を参照してください。
これは thread2 - 破棄可能なスレッドに基づいています(Pythonレシピ)
PyThreadState_SetasyncExc()を呼び出す必要があります。これはctypesを通してのみ利用可能です。
これはPython 2.7.3でのみテストされていますが、最近の他の2.xリリースでも動作する可能性があります。
import ctypes
def terminate_thread(thread):
"""Terminates a python thread from another thread.
:param thread: a threading.Thread instance
"""
if not thread.isAlive():
return
exc = ctypes.py_object(SystemExit)
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(
ctypes.c_long(thread.ident), exc)
if res == 0:
raise ValueError("nonexistent thread id")
Elif res > 1:
# """if it returns a number greater than one, you're in trouble,
# and you should call it again with exc=NULL to revert the effect"""
ctypes.pythonapi.PyThreadState_SetAsyncExc(thread.ident, None)
raise SystemError("PyThreadState_SetAsyncExc failed")
スレッドに協力しない限り、スレッドを強制的に強制終了してはいけません。
スレッドを強制終了すると、設定されている/最後にブロックされたブロックを保証することがなくなるため、ロックをロックしたままにしたり、ファイルを開いたままにしたりできます。
スレッドを強制的に強制終了することをお勧めできるのは、プログラムを高速に強制終了することですが、単一スレッドでは絶対に実行できないのです。
他の人が述べたように、標準は停止フラグを立てることです。軽量なもの(Threadのサブクラス化もグローバル変数もない)では、ラムダコールバックがオプションです。 (if stop()
の括弧に注意してください。)
import threading
import time
def do_work(id, stop):
print("I am thread", id)
while True:
print("I am thread {} doing something".format(id))
if stop():
print(" Exiting loop.")
break
print("Thread {}, signing off".format(id))
def main():
stop_threads = False
workers = []
for id in range(0,3):
tmp = threading.Thread(target=do_work, args=(id, lambda: stop_threads))
workers.append(tmp)
tmp.start()
time.sleep(3)
print('main: done sleeping; time to stop the threads.')
stop_threads = True
for worker in workers:
worker.join()
print('Finis.')
if __== '__main__':
main()
print()
を常にフラッシュするpr()
関数(sys.stdout.flush()
)に置き換えると、シェル出力の精度が向上する可能性があります。
(Windows/Eclipse/Python3.3でのみテスト済み)
Pythonでは、単にThreadを直接殺すことはできません。
あなたが本当にThread(!)を持っている必要がないならば、 threading package を使う代わりに、 multiprocessing packageを使うことができます。 。ここでは、プロセスを強制終了するために、単純にメソッドを呼び出すことができます。
yourProcess.terminate() # kill the process!
Pythonはあなたのプロセスを終了させるでしょう(UnixではSIGTERMシグナルを通して、WindowsではTerminateProcess()
呼び出しを通して)。キューやパイプを使用している間それを使用するように注意を払ってください! (Queue/Pipe内のデータが破損する可能性があります)
multiprocessing.Event
とmultiprocessing.Semaphore
は、それぞれthreading.Event
とthreading.Semaphore
とまったく同じように機能します。事実、最初のものはラターのクローンです。
本当にスレッドを使う必要があるのなら、それを直接殺す方法はありません。しかし、あなたができることは "デーモンスレッド" を使うことです。実際、Pythonでは、スレッドに daemon のフラグを立てることができます。
yourThread.daemon = True # set the Thread as a "daemon thread"
生きている非デーモンスレッドがなくなると、メインプログラムは終了します。言い換えれば、あなたのメインスレッド(もちろん、非デーモンスレッド)がその操作を終了するとき、まだいくつかのデーモンスレッドが動作していてもプログラムは終了するでしょう。
start()
メソッドが呼び出される前に、Threadをdaemon
として設定する必要があることに注意してください。
もちろんdaemon
でもmultiprocessing
を使うことができますし、そうすべきです。ここでは、メインプロセスが終了すると、デーモンプロセスのすべての子プロセスを終了しようとします。
最後に、sys.exit()
とos.kill()
は選択肢ではありません。
スレッドを終了するスレッドにtraceをインストールすることでスレッドを強制終了できます。 1つの可能な実装のために添付のリンクを見てください。
あなたがスレッドを殺さないならそれはより良いです。 1つの方法は、スレッドのサイクルに "try"ブロックを導入し、スレッドを停止したいときに例外をスローすることです(例えば、break/return/...はfor/while/...を停止します)。私は自分のアプリでこれを使用しましたが、うまくいきました...
次のコード例のようにThread.stop
メソッドを実装することは間違いなく可能です。
import sys
import threading
import time
class StopThread(StopIteration):
pass
threading.SystemExit = SystemExit, StopThread
class Thread2(threading.Thread):
def stop(self):
self.__stop = True
def _bootstrap(self):
if threading._trace_hook is not None:
raise ValueError('Cannot run thread with tracing!')
self.__stop = False
sys.settrace(self.__trace)
super()._bootstrap()
def __trace(self, frame, event, arg):
if self.__stop:
raise StopThread()
return self.__trace
class Thread3(threading.Thread):
def _bootstrap(self, stop_thread=False):
def stop():
nonlocal stop_thread
stop_thread = True
self.stop = stop
def tracer(*_):
if stop_thread:
raise StopThread()
return tracer
sys.settrace(tracer)
super()._bootstrap()
###############################################################################
def main():
test1 = Thread2(target=printer)
test1.start()
time.sleep(1)
test1.stop()
test1.join()
test2 = Thread2(target=speed_test)
test2.start()
time.sleep(1)
test2.stop()
test2.join()
test3 = Thread3(target=speed_test)
test3.start()
time.sleep(1)
test3.stop()
test3.join()
def printer():
while True:
print(time.time() % 1)
time.sleep(0.1)
def speed_test(count=0):
try:
while True:
count += 1
except StopThread:
print('Count =', count)
if __== '__main__':
main()
Thread3
クラスは、Thread2
クラスより約33%高速にコードを実行するように見えます。
from ctypes import *
pthread = cdll.LoadLibrary("libpthread-2.15.so")
pthread.pthread_cancel(c_ulong(t.ident))
t はあなたのThread
name__オブジェクトです。
Modules/threadmodule.c
がPython/thread_pthread.h
型であることがわかるpythonソース(Thread.ident
とpthread_t
)を読んでください。そのため、pthread
name__がpythonでlibpthread
name__を使用してできることは何でもできます。
スレッドの一部としてtime.sleep()
を明示的に呼び出している場合(外部サービスのポーリングなど)、Phillipeのメソッドの改善は、wait()
のどこでもevent
のsleep()
メソッドでタイムアウトを使用することです
例えば:
import threading
class KillableThread(threading.Thread):
def __init__(self, sleep_interval=1):
super().__init__()
self._kill = threading.Event()
self._interval = sleep_interval
def run(self):
while True:
print("Do Something")
# If no kill signal is set, sleep for the interval,
# If kill signal comes in while sleeping, immediately
# wake up and handle
is_killed = self._kill.wait(self._interval)
if is_killed:
break
print("Killing Thread")
def kill(self):
self._kill.set()
それからそれを実行する
t = KillableThread(sleep_interval=5)
t.start()
# Every 5 seconds it prints:
#: Do Something
t.kill()
#: Killing Thread
wait()
ingの代わりにsleep()
を使用して定期的にイベントをチェックすることの利点は、より長いスリープ間隔でプログラムできることです。スレッドはほとんどすぐに停止します(そうでなければsleep()
ingになります)。よりシンプル。
次の回避策を使用してスレッドを強制終了することができます。
kill_threads = False
def doSomething():
global kill_threads
while True:
if kill_threads:
thread.exit()
......
......
thread.start_new_thread(doSomething, ())
メインスレッドから他のモジュールにコードが書き込まれているスレッドを終了させる場合にも使用できます。そのモジュール内でグローバル変数を宣言し、それを使用してそのモジュール内で生成されたスレッドを終了させることができます。
通常、これを使用してプログラムの終了時にすべてのスレッドを終了します。これはスレッドを終了するための完璧な方法ではないかもしれませんが、助けになるかもしれません。
私が付け加えたいのは、公式ドキュメントを threading lib Python で読んだ場合、Paoloというフラグを付けて、スレッドを突然終了させたくない場合は、 "demonic"スレッドの使用を避けることをお勧めします。 Rovelli 言及した 。
公式文書より:
デーモンスレッドはシャットダウン時に突然停止します。それらのリソース(開いているファイル、データベーストランザクションなど)が正しく解放されていない可能性があります。スレッドを適切に停止させたい場合は、それらを非デーモンにして、Eventなどの適切なシグナリングメカニズムを使用してください。
デーモンスレッドを作成することはあなたのアプリケーションに依存すると思いますが、一般的に(そして私の意見では)それらを殺したりデーモンにしたりするのは避けたほうがいいです。マルチプロセッシングでは、is_alive()
を使用してプロセスのステータスをチェックし、それらを終了するために「終了」することができます(GILの問題も回避できます)。しかし、Windowsでコードを実行したときに、もっと問題が見つかることがあります。
そして、あなたが「ライブスレッド」を持っているなら、Pythonインタプリタはそれらを待つために走っていることを常に覚えていてください。 (このデーモンのおかげで問題が突然終わらなくても助けることができます)。
私はこのゲームには間に合いませんが、 と同様の質問 と格闘していました、そして以下は両方とも問題を完璧に解決してくれるようです。スレッド終了:
import threading
import time
import atexit
def do_work():
i = 0
@atexit.register
def goodbye():
print ("'CLEANLY' kill sub-thread with value: %s [THREAD: %s]" %
(i, threading.currentThread().ident))
while True:
print i
i += 1
time.sleep(1)
t = threading.Thread(target=do_work)
t.daemon = True
t.start()
def after_timeout():
print "KILL MAIN THREAD: %s" % threading.currentThread().ident
raise SystemExit
threading.Timer(2, after_timeout).start()
収量:
0
1
KILL MAIN THREAD: 140013208254208
'CLEANLY' kill sub-thread with value: 2 [THREAD: 140013674317568]
この目的のために構築されたライブラリ、 stopit があります。ここに記載されている同じ注意のいくつかはまだ適用されますが、少なくともこのライブラリは述べられた目的を達成するための定期的で繰り返し可能な技術を提示します。
それはかなり古いですが、 これ はある人にとっては便利な解決策かもしれません:
スレッドのモジュール機能を拡張する小さなモジュール - あるスレッドが別のスレッドのコンテキストで例外を発生させることを可能にします。
SystemExit
を上げることで、ついにpythonスレッドを殺すことができます。
import threading
import ctypes
def _async_raise(tid, excobj):
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(excobj))
if res == 0:
raise ValueError("nonexistent thread id")
Elif res > 1:
# """if it returns a number greater than one, you're in trouble,
# and you should call it again with exc=NULL to revert the effect"""
ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0)
raise SystemError("PyThreadState_SetAsyncExc failed")
class Thread(threading.Thread):
def raise_exc(self, excobj):
assert self.isAlive(), "thread must be started"
for tid, tobj in threading._active.items():
if tobj is self:
_async_raise(tid, excobj)
return
# the thread was alive when we entered the loop, but was not found
# in the dict, hence it must have been already terminated. should we raise
# an exception here? silently ignore?
def terminate(self):
# must raise the SystemExit type, instead of a SystemExit() instance
# due to a bug in PyThreadState_SetAsyncExc
self.raise_exc(SystemExit)
そのため、「スレッドが別のスレッドのコンテキストで例外を発生させる」ことが可能になり、このようにして、終了したスレッドは、アボートフラグを定期的にチェックすることなく終了を処理できます。
しかし、 オリジナルのソース によると、このコードにはいくつかの問題があります。
- 例外はpythonバイトコードを実行しているときにのみ発生します。あなたのスレッドがネイティブ/ビルトインブロッキング関数を呼び出した場合、例外は実行がpythonコードに戻ったときにだけ発生します。
- 組み込み関数が内部的にPyErr_Clear()を呼び出すと、保留中の例外が事実上キャンセルされるという問題もあります。もう一度上げることができます。
- 例外タイプのみ安全に発生させることができます。例外インスタンスは予期しない動作を引き起こす可能性があるため、制限されています。
- たとえば、t1.raise_exc(TypeError)ではなくt1.raise_exc(TypeError( "blah"))です。
- 私見それはバグです、そして私はそれを一つとして報告しました。詳細については、 http://mail.python.org/pipermail/python-dev/2006-August/068158.html
- この関数を組み込みのスレッドモジュールで公開するように依頼しましたが、ctypesは標準ライブラリ(2.5以降)になりました。
機能は実装に依存しない可能性があります。保持される場合があります
未公開。
SetDaemon(True)でサブスレッドを起動します。
def bootstrap(_filename):
mb = ModelBootstrap(filename=_filename) # Has many Daemon threads. All get stopped automatically when main thread is stopped.
t = threading.Thread(target=bootstrap,args=('models.conf',))
t.setDaemon(False)
while True:
t.start()
time.sleep(10) # I am just allowing the sub-thread to run for 10 sec. You can listen on an event to stop execution.
print('Thread stopped')
break
これは悪い答えです、コメントを見てください
これを行う方法は次のとおりです。
from threading import *
...
for thread in enumerate():
if thread.isAlive():
try:
thread._Thread__stop()
except:
print(str(thread.getName()) + ' could not be terminated'))
数秒後にスレッドを停止します。 thread._Thread__delete()
メソッドも確認してください。
私は便宜上thread.quit()
メソッドをお勧めします。たとえば、スレッドにソケットがある場合は、ソケットハンドルクラスにquit()
メソッドを作成し、ソケットを終了してから、thread._Thread__stop()
内でquit()
を実行することをお勧めします。
これは、Windows 7上のpywin32で動作するようです
my_thread = threading.Thread()
my_thread.start()
my_thread._Thread__stop()
ØMQ - プロジェクトの創設者の1人であるPieter Hintjensは、ØMQを使用し、ロック、ミューテックス、イベントなどの同期プリミティブを回避することが、マルチスレッドプログラムを作成する最も安全で安全な方法です。
http://zguide.zeromq.org/py:all#Multithreading-with-ZeroMQ
これには、子スレッドにその作業を取り消すように指示することも含まれます。これは、スレッドにØMQソケットを装着し、そのソケットでキャンセルするように指示するメッセージをポーリングすることによって行われます。
このリンクは、ØMQを使用したマルチスレッドPythonコードの例も示しています。