web-dev-qa-db-ja.com

スレッドを殺す方法はありますか?

フラグ/セマフォなどを設定/チェックせずに実行中のスレッドを終了させることは可能ですか?

656
Sudden Def

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インタプリタの外部でビジー状態になっている場合は中断をキャッチしないため、これは魔法の弾丸ではありません。

このコードのよい使用パターンは、スレッドに特定の例外をキャッチさせてクリーンアップを実行させることです。そうすれば、タスクを中断しても適切なクリーンアップを実行できます。

599
Philippe F

それをするための公式のAPIはありません。

スレッドを強制終了するにはプラットフォームAPIを使用する必要があります。 pthread_kill、またはTerminateThread。あなたはそのようなAPIにアクセスすることができます。 pythonwinを通して、またはctypesを通して。

これは本質的に危険です。それはおそらく(ゴミになるスタックフレームのローカル変数から)収集不可能なゴミを招くでしょうし、殺されたスレッドが殺された時点でGILを持っていると、デッドロックを招くかもしれません。

105

multiprocessing.Processp.terminate()できます

私がスレッドを強制終了したいが、フラグ/ロック/シグナル/セマフォ/イベント/その他を使用したくない場合は、スレッドを本格的なプロセスに昇格させます。ほんの数スレッドを使用するコードの場合、オーバーヘッドはそれほど悪くありません。

例えば。ブロッキングI/Oを実行するヘルパー「スレッド」を簡単に終了させるのに便利です。

変換は簡単です。関連するコードで、すべてのthreading.Threadmultiprocessing.Processに、すべてのqueue.Queuemultiprocessing.Queueに置き換え、必要なp.terminate()の呼び出しを親プロセスに追加して、その子プロセスをp

Pythonドキュメント

78
cfi

プログラム全体を終了させようとしているのなら、スレッドを「デーモン」として設定することができます。 Thread.daemon を参照してください。

61
schettino72

これは 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")
33
Johan Dahlin

スレッドに協力しない限り、スレッドを強制的に強制終了してはいけません。

スレッドを強制終了すると、設定されている/最後にブロックされたブロックを保証することがなくなるため、ロックをロックしたままにしたり、ファイルを開いたままにしたりできます。

スレッドを強制的に強制終了することをお勧めできるのは、プログラムを高速に強制終了することですが、単一スレッドでは絶対に実行できないのです。

他の人が述べたように、標準は停止フラグを立てることです。軽量なもの(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でのみテスト済み)

28
Jon Coombs

Pythonでは、単にThreadを直接殺すことはできません。

あなたが本当にThread(!)を持っている必要がないならば、 threading package を使う代わりに、 multiprocessing packageを使うことができます。 。ここでは、プロセスを強制終了するために、単純にメソッドを呼び出すことができます。

yourProcess.terminate()  # kill the process!

Pythonはあなたのプロセスを終了させるでしょう(UnixではSIGTERMシグナルを通して、WindowsではTerminateProcess()呼び出しを通して)。キューやパイプを使用している間それを使用するように注意を払ってください! (Queue/Pipe内のデータが破損する可能性があります)

multiprocessing.Eventmultiprocessing.Semaphoreは、それぞれthreading.Eventthreading.Semaphoreとまったく同じように機能します。事実、最初のものはラターのクローンです。

本当にスレッドを使う必要があるのなら、それを直接殺す方法はありません。しかし、あなたができることは "デーモンスレッド" を使うことです。実際、Pythonでは、スレッドに daemon のフラグを立てることができます。

yourThread.daemon = True  # set the Thread as a "daemon thread"

生きている非デーモンスレッドがなくなると、メインプログラムは終了します。言い換えれば、あなたのメインスレッド(もちろん、非デーモンスレッド)がその操作を終了するとき、まだいくつかのデーモンスレッドが動作していてもプログラムは終了するでしょう。

start()メソッドが呼び出される前に、Threadをdaemonとして設定する必要があることに注意してください。

もちろんdaemonでもmultiprocessingを使うことができますし、そうすべきです。ここでは、メインプロセスが終了すると、デーモンプロセスのすべての子プロセスを終了しようとします。

最後に、sys.exit()os.kill()は選択肢ではありません。

21
Paolo Rovelli

スレッドを終了するスレッドにtraceをインストールすることでスレッドを強制終了できます。 1つの可能な実装のために添付のリンクを見てください。

Pythonでスレッドを殺す

13
Kozyarchuk

あなたがスレッドを殺さないならそれはより良いです。 1つの方法は、スレッドのサイクルに "try"ブロックを導入し、スレッドを停止したいときに例外をスローすることです(例えば、break/return/...はfor/while/...を停止します)。私は自分のアプリでこれを使用しましたが、うまくいきました...

9
Giancarlo

次のコード例のように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%高速にコードを実行するように見えます。

8
Noctis Skytower
from ctypes import *
pthread = cdll.LoadLibrary("libpthread-2.15.so")
pthread.pthread_cancel(c_ulong(t.ident))

t はあなたのThreadname__オブジェクトです。

Modules/threadmodule.cPython/thread_pthread.h型であることがわかるpythonソース(Thread.identpthread_t)を読んでください。そのため、pthreadname__がpythonでlibpthreadname__を使用してできることは何でもできます。

6
snyh

スレッドの一部としてtime.sleep()を明示的に呼び出している場合(外部サービスのポーリングなど)、Phillipeのメソッドの改善は、wait()のどこでもeventsleep()メソッドでタイムアウトを使用することです

例えば:

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になります)。よりシンプル。

6
SCB

次の回避策を使用してスレッドを強制終了することができます。

kill_threads = False

def doSomething():
    global kill_threads
    while True:
        if kill_threads:
            thread.exit()
        ......
        ......

thread.start_new_thread(doSomething, ())

メインスレッドから他のモジュールにコードが書き込まれているスレッドを終了させる場合にも使用できます。そのモジュール内でグローバル変数を宣言し、それを使用してそのモジュール内で生成されたスレッドを終了させることができます。

通常、これを使用してプログラムの終了時にすべてのスレッドを終了します。これはスレッドを終了するための完璧な方法ではないかもしれませんが、助けになるかもしれません。

5
Amit Chahar

私が付け加えたいのは、公式ドキュメントを threading lib Python で読んだ場合、Paoloというフラグを付けて、スレッドを突然終了させたくない場合は、 "demonic"スレッドの使用を避けることをお勧めします。 Rovelli 言及した

公式文書より:

デーモンスレッドはシャットダウン時に突然停止します。それらのリソース(開いているファイル、データベーストランザクションなど)が正しく解放されていない可能性があります。スレッドを適切に停止させたい場合は、それらを非デーモンにして、Eventなどの適切なシグナリングメカニズムを使用してください。

デーモンスレッドを作成することはあなたのアプリケーションに依存すると思いますが、一般的に(そして私の意見では)それらを殺したりデーモンにしたりするのは避けたほうがいいです。マルチプロセッシングでは、is_alive()を使用してプロセスのステータスをチェックし、それらを終了するために「終了」することができます(GILの問題も回避できます)。しかし、Windowsでコードを実行したときに、もっと問題が見つかることがあります。

そして、あなたが「ライブスレッド」を持っているなら、Pythonインタプリタはそれらを待つために走っていることを常に覚えていてください。 (このデーモンのおかげで問題が突然終わらなくても助けることができます)。

4
Chema

私はこのゲームには間に合いませんが、 と同様の質問 と格闘していました、そして以下は両方とも問題を完璧に解決してくれるようです。スレッド終了:

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]
4
slumtrimpet

この目的のために構築されたライブラリ、 stopit があります。ここに記載されている同じ注意のいくつかはまだ適用されますが、少なくともこのライブラリは述べられた目的を達成するための定期的で繰り返し可能な技術を提示します。

1
Jason R. Coombs

それはかなり古いですが、 これ はある人にとっては便利な解決策かもしれません:

スレッドのモジュール機能を拡張する小さなモジュール - あるスレッドが別のスレッドのコンテキストで例外を発生させることを可能にします。 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()を呼び出すと、保留中の例外が事実上キャンセルされるという問題もあります。もう一度上げることができます。
  • 例外タイプのみ安全に発生させることができます。例外インスタンスは予期しない動作を引き起こす可能性があるため、制限されています。
  • この関数を組み込みのスレッドモジュールで公開するように依頼しましたが、ctypesは標準ライブラリ(2.5以降)になりました。
    機能は実装に依存しない可能性があります。保持される場合があります
    未公開。
0
wp78de

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
0
Sud

これは悪い答えです、コメントを見てください

これを行う方法は次のとおりです。

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()を実行することをお勧めします。

0
DoXiD

これは、Windows 7上のpywin32で動作するようです

my_thread = threading.Thread()
my_thread.start()
my_thread._Thread__stop()
0
zzart

ØMQ - プロジェクトの創設者の1人であるPieter Hintjensは、ØMQを使用し、ロック、ミューテックス、イベントなどの同期プリミティブを回避することが、マルチスレッドプログラムを作成する最も安全で安全な方法です。

http://zguide.zeromq.org/py:all#Multithreading-with-ZeroMQ

これには、子スレッドにその作業を取り消すように指示することも含まれます。これは、スレッドにØMQソケットを装着し、そのソケットでキャンセルするように指示するメッセージをポーリングすることによって行われます。

このリンクは、ØMQを使用したマルチスレッドPythonコードの例も示しています。

0
paulkernstock