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


回答:


672

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,  *args, **kwargs):
        super(StoppableThread, self).__init__(*args, **kwargs)
        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 )

(に基づいてKillableスレッド Tomer Filibaによって。の戻り値についての引用PyThreadState_SetAsyncExcが表示されますが、からのものであるとのPythonの古いバージョン。)

ドキュメントに記載されているように、これは魔法の弾丸ではありません。スレッドがPythonインタープリターの外部でビジー状態の場合、割り込みをキャッチしないからです。

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


78
@ Bluebird75:さらに、「スレッドは適切に閉じる必要のある重要なリソースを保持している可能性があるため」スレッドが突然強制終了されるべきではないという主張が得られるかどうかわかりません。これはメインプログラムやメインプログラムからも当てはまります。ユーザー(たとえば、UNIXではCtrl-C)によって突然殺される可能性があります。その場合、ユーザーはこの可能性を可能な限りうまく処理しようとします。ですから、スレッドの何が特別で、なぜメインプログラムと同じ扱いを受けるべきではないのか(つまり、スレッドが突然強制終了される可能性がある)を理解できません。:)これについて詳しく説明してもらえますか?
Eric O Lebigot

18
@EOL:一方、スレッドが所有しているすべてのリソースがローカルリソース(開いているファイル、ソケット)である場合、Linuxはプロセスのクリーンアップに適しており、これはリークしません。ソケットを使用してサーバーを作成した場合がありましたが、Ctrl-Cで残忍な中断をすると、ソケットをバインドできないため、プログラムを起動できなくなりました。5分待つ必要があります。適切な解決策は、Ctrl-Cをキャッチし、クリーンなソケットの切断を行うことでした。
フィリップF

10
@ Bluebird75:ところで。エラーSO_REUSEADDRを回避するためにソケットオプションを使用できAddress already in useます。
Messa

12
この回答について注意してください:少なくとも私(py2.6)では、ケースのNone代わりに渡す必要があり、それを呼び出して、tidを直接ではなく任意のctypes関数に渡さなければなりませんでした。0res != 1ctypes.c_long(tid)
Walt W

21
_stopはすでにPython 3スレッドライブラリで占有されていることを言及する価値があります。そのため、別の変数を使用すると、エラーが発生します。
2013年

113

それを行う公式のAPIはありません。

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

これは本質的に安全ではないことに注意してください。削除されたスレッドが削除された時点でGILがある場合、(収集されたスタックフレームのローカル変数から)収集不能なガベージが発生し、デッドロックが発生する可能性があります。


26
それはなります問題のスレッドはGILを保持している場合、デッドロックにつながります。
Matthias Urlichs、2015

96

multiprocessing.Processp.terminate()

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

たとえば、これはブロッキングI / Oを実行するヘルパー「スレッド」を簡単に終了するのに便利です

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

Pythonドキュメントをmultiprocessing参照してください。


ありがとう。queue.Queueをmultiprocessing.JoinableQueueに置き換え、この回答に従いました:stackoverflow.com/a/11984760/911207
David Braun

この問題に関する多くのページ。これは私が考える多くの人にとって明白な解決策のようです
地質学2016年

6
multiprocessingいいですが、引数は新しいプロセスにピクルされます。したがって、引数の1つが(のようにlogging.log)ピッキングできないものである場合、を使用することはお勧めできませんmultiprocessing
Lyager

1
multiprocessing引数はWindowsの新しいプロセスにピクルされますが、Linuxはforkを使用してそれらをコピーします(Python 3.7、他のバージョンは不明)。つまり、Linuxでは機能しますが、Windowsではピクルエラーが発生するコードになります。
nyanpasu64

multiprocessingロギングを使用するのは難しいビジネスです。使用する必要がありますQueueHandlerこのチュートリアルを参照)。私はそれを難し​​い方法で学びました。
Fanchen Bao

74

プログラム全体を終了しようとしている場合は、スレッドを「デーモン」として設定できます。Thread.daemonを参照


これは意味がありません。ドキュメントには、「start()が呼び出される前に設定する必要があります。そうしないと、RuntimeErrorが発生します」と明記されています。したがって、もともとデーモンではなかったスレッドを強制終了したい場合、どうすればこれを使用できますか?
Raffi Khatchadourian、2011年

27
ラフィは、メインスレッドが終了するとデーモンスレッドも終了することを知っているため、事前に設定することをお勧めしていると思います。
2014

1
なぜこれが受け入れられない答えなのかわからない
エリック

メインプログラムがシャットダウンした場合でもスレッドを実行し続けたい場合に備えて、スレッドをデーモンとして設定しませんか?
ミケーレピッコリーニ

あなたはサー、その日のヒーローです。まさに私が探していたものであり、追加するのに全く手間がかかりません。
Blizz

42

他の人が述べたように、標準は停止フラグを設定することです。軽量なもの(スレッドのサブクラス化なし、グローバル変数なし)の場合、ラムダコールバックはオプションです。(の括弧に注意してください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 __name__ == '__main__':
    main()

常にフラッシュする()関数で置き換えるprint()と、シェル出力の精度が向上する場合があります。pr()sys.stdout.flush()

(Windows / Eclipse / Python3.3でのみテスト済み)


1
Linux / Python 2.7で検証され、魅力のように動作します。これは公式の答えであるはずです、それははるかに簡単です。
ポールケンジョラ2017年

1
Linux Ubuntu Server 17.10 / Python 3.6.3で検証され、動作します。
Marcos

1
2.7でも検証されています。そのような素晴らしい答え!
silgon 2018

pr()機能とは?
アルパー

35

これはthread2-killableスレッド(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")

私はこのようなものを使用して、スレッドKeyboardInterruptにクリーンアップする機会を与えています。その後もハングしている場合SystemExitは、適切であるか、ターミナルからプロセスを強制終了します。
drevicko 2013年

これは、スレッドが現在実行中の場合に機能します。スレッドがsyscallにある場合は機能しません。例外は黙って無視されます。
Matthias Urlichs 2014年

@MatthiasUrlichsは、スレッドの実行状態を検出して、警告を出力したり再試行したりする方法を教えてください。
ヨハンダーリン2014

1
@JohanDahlin少し待って(再試行する場合はとにかく行う必要があります)、isAlive()テストを実行します。いずれにせよ、これは機能しますが、ぶら下がっている参照が残っていないことも保証しません。理論的には、CPythonでスレッドキルを安全に実行することは可能ですが、を慎重に使用するとpthread_cleanup_push()/_pop()、正しく実装するのに多くの作業が必要になり、インタープリターの速度が著しく低下します。
Matthias Urlichs 2014

32

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

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

スレッドを強制的に強制終了するのが良い考えであると主張できるのは、プログラムを高速に強制終了することですが、単一のスレッドを強制することはありません。


11
スレッドに伝えるのが難しいのはなぜですか。現在のループが終了したら、自殺してください...わかりません。
Mehdi

2
そのような「ループ」を識別するためにCPUに組み込まれたメカニズムはありません。あなたが期待できる最善の方法は、現在ループ内にあるコードが終了するとチェックする何らかの信号を使用することです。スレッドの同期を処理する正しい方法は協調的な方法によるものであり、スレッドの中断、再開、および強制終了は、アプリケーションコードではなく、デバッガーおよびオペレーティングシステム向けの関数です。
Lasse V. Karlsen

2
@Mehdi:私が(個人的に)スレッドでコードを書いている場合、はい、同意します。しかし、サードパーティのライブラリを実行しているケースがあり、そのコードの実行ループにアクセスできません。これは、要求された機能の1つの使用例です。
Dan H

@DanH サードパーティのコードでは、それがどのような損害を与える可能性があるかわからないため、最悪です。サードパーティのライブラリが十分に堅牢ではないため、強制終了する必要がある場合は、次のいずれかを実行する必要があります。(1)作成者に問題の修正を依頼する、(2)別の方法を使用する。一部のリソースは単一のプロセス内でのみ共有されるため、本当に選択肢がない場合は、そのコードを別のプロセスに置く方が安全です。
Phil1970

25

Pythonでは、スレッドを直接強制終了することはできません。

スレッド(!)が本当に必要ない場合、スレッドパッケージを使用する代わりに、マルチプロセッシングパッケージを使用する ことができます。ここで、プロセスを強制終了するには、メソッドを呼び出すだけです。

yourProcess.terminate()  # kill the process!

Pythonはプロセスを強制終了します(UNIXではSIGTERMシグナルを使用し、WindowsではTerminateProcess()呼び出しを使用)。キューまたはパイプの使用中は注意してください。(それはキュー/パイプのデータを破壊するかもしれません)

なお、multiprocessing.Event及びmultiprocessing.Semaphore正確に同じように仕事threading.Eventthreading.Semaphoreそれぞれ。実際、最初のものは後者のクローンです。

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

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

デーモン以外の生きているスレッドがなくなると、メインプログラムは終了します。つまり、メインスレッド(もちろん、非デーモンスレッド)がその操作を完了すると、まだいくつかのデーモンスレッドが動作していても、プログラムは終了します。

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

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

最後に、注意してください。これは選択肢sys.exit()os.kill()はありません。


14

スレッドを終了するスレッドにトレースをインストールすると、スレッドを強制終了できます。1つの可能な実装については、添付のリンクを参照してください。

Pythonでスレッドを強制終了する


私はすでにそれを見ました。このソリューションは、self.killedフラグチェックに基づいています
Sudden Def

1
実際に機能する数少ない回答の1つ
Ponkadoodle 2013

5
このソリューションには2つの問題があります。(a)sys.settrace()を使用してトレーサーをインストールすると、スレッドの実行が遅くなります。計算限界の場合、10倍も遅くなります。(b)システムコール中のスレッドには影響しません。
Matthias Urlichs 2014年

10

あなたが明示的に呼び出している場合はtime.sleep()、あなたのスレッドの一部(ポーリングいくつかの外部サービスと言う)として、フィリップの方法に改善がでタイムアウトを使用することですeventさんwait()どこ方法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()sleep()ingして定期的にチェックする代わりにを使用する利点は、より長いスリープ間隔でプログラムできること、スレッドがほとんど即座に停止すること(そうでなければsleep()ingしている場合)、そして私の意見では、exitを処理するためのコードは非常に簡単です。 。


3
なぜこの投稿は反対投票されたのですか?この投稿の何が問題になっていますか?それは私が必要としているものとまったく同じに見えます...
JDOaktown '29

9

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


8

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 __name__ == '__main__':
    main()

Thread3クラスは約33%よりも速くコードを実行するように見えるThread2クラス。


3
これはself.__stop、スレッドに設定されているかどうかのチェックを挿入する賢い方法です。ここでの他のほとんどのソリューションと同様に、トレース関数は新しいローカルスコープに入るときにのみ呼び出されるため、実際にはブロッキング呼び出しを中断しません。また注目に値するのは、sys.settraceデバッガーやプロファイルなどを実装するためのものであり、CPythonの実装の詳細と見なされ、他のPython実装に存在することが保証されていないことです。
dano

3
@dano:Thread2クラスの最大の問題の1つは、コードの実行が約10倍遅くなることです。一部の人々はまだこれが許容できると思うかもしれません。
Noctisスカイタワー2014

これを+1すると、コードの実行がかなり遅くなります。このソリューションの作成者がこの情報を回答に含めることをお勧めします。
Vishal

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

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

Pythonソース(Modules/threadmodule.cおよびPython/thread_pthread.h)を読んThread.identで、pthread_t型であることを確認pthreadできるので、Pythonを使用して何でもできるようになりますlibpthread


そして、Windowsでこれをどのように行いますか?
iChux 2014

12
あなたはしません。WindowsでもLinuxでもありません。理由:これを行っている間、問題のスレッドがGILを保持している可能性があります(PythonがCを呼び出すとGILが解放されます)。その場合、プログラムは即座にデッドロックします。そうでない場合でも、最終的には、ブロックは実行されない、などなので、これは非常に安全ではありません。
Matthias Urlichs 2014年

6

スレッドを強制終了するには、次の回避策を使用できます。

kill_threads = False

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

thread.start_new_thread(doSomething, ())

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

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


いいぞ。理解しやすいです。
alyssaeliyah 2018年

6

私はこのゲームにかなり遅れていますが、同様の質問に取り組んでおり、次のように問題を完全に解決し、デーモン化されたサブスレッドが終了したときに基本的なスレッド状態のチェックとクリーンアップを実行できます。

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]

これは優れた答えであり、リストの上位になるはずです
drootang

スレッドSystemExitで発生すると、なぜafter_timeoutメインスレッドに対して何かが行われるのですか(この例では、前のスレッドが終了するのを単に待機しています)。
Davis Herring

@DavisHerring私はあなたが何を得ているのかわかりません。SystemExitはメインスレッドを強制終了します。なぜメインスレッドで何かを実行すると思いますか?その呼び出しがなければ、プログラムは子スレッドで待機し続けるだけです。Ctrl + Cキーを押すか、他の方法を使用してメインスレッドを強制終了することもできますが、これは一例です。
slumtrimpet

@slumtrimpet:SystemExit特別なプロパティは2つだけです。トレースバックを生成せず(スレッドを1つスローして終了した場合)、メインスレッドが1つスローして終了した場合は、終了ステータスを設定します(それでも他の非デーモンスレッドを待機しています)。出る)。
Davis Herring

@DavisHerringああ、私はおかしい。完全に同意します。私たちはここで私たちがしたことを思い出し、あなたのコメントを誤解していました。
スラムトランペット

4

追加したいことの1つは、スレッド化lib Pythonの公式ドキュメントを読む場合、Paolo Rovelliが言及したフラグを使用してスレッドを突然終了させたくない場合は、「悪魔的」スレッドの使用を避けることをお勧めします。

公式ドキュメントから:

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

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

また、「ライブスレッド」がある場合は、Pythonインタープリターが待機して実行されることを忘れないでください。(このデーモンが突然問題でなくてもあなたを助けることができるので)。


4
私は最後の段落を理解していません。
tshepang 14

@Tshepangこれは、アプリケーションに実行中の非デーモンスレッドがある場合、すべての非デーモンスレッドが完了するまでPythonインタープリターが実行を継続することを意味します。プログラムの終了時にスレッドが終了するかどうかを気にしない場合は、スレッドをデーモンにすると便利です。
トムミデルティン2016年

3

この目的のために構築されたライブラリstopitがあります。ここに記載されているものと同じ注意事項の一部が依然として適用されますが、少なくともこのライブラリは、指定された目標を達成するための定期的で反復可能な手法を提供します。


1

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

スレッディングのモジュール機能を拡張する小さなモジュール。あるスレッドが別のスレッドのコンテキストで例外を発生させることができます。を上げると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

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

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

0

ピエター・ヒントジェンズ-の創始者の一人ØMQ -project - ØMQを使用して、と言うとロック、ミューテックス、イベントなどのような同期プリミティブを避け、マルチスレッドプログラムを書くためのsanestとsecurest方法です。

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

これには、子スレッドに作業をキャンセルするように伝えることが含まれます。これは、スレッドにØMQソケットを装備し、キャンセルする必要があることを示すメッセージをそのソケットでポーリングすることによって行われます。

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


0

同じ機能の複数のスレッドが必要であると仮定すると、これはIDHOで停止するための最も簡単な実装です。

import time
from threading import Thread

def doit(id=0):
    doit.stop=0
    print("start id:%d"%id)
    while 1:
        time.sleep(1)
        print(".")
        if doit.stop==id:
            doit.stop=0
            break
    print("end thread %d"%id)

t5=Thread(target=doit, args=(5,))
t6=Thread(target=doit, args=(6,))

t5.start() ; t6.start()
time.sleep(2)
doit.stop =5  #kill t5
time.sleep(2)
doit.stop =6  #kill t6

いいことはここにあります。同じ機能と異なる機能を複数持つことができ、すべてを停止することができます functionname.stop

関数のスレッドを1つだけにする場合は、IDを覚えておく必要はありません。doit.stop0より大きい場合は停止してください。


0

@SCBのアイデア(まさに私が必要とするもの)を基にして、カスタマイズされた関数でKillableThreadサブクラスを作成するだけです。

from threading import Thread, Event

class KillableThread(Thread):
    def __init__(self, sleep_interval=1, target=None, name=None, args=(), kwargs={}):
        super().__init__(None, target, name, args, kwargs)
        self._kill = Event()
        self._interval = sleep_interval
        print(self._target)

    def run(self):
        while True:
            # Call custom function with arguments
            self._target(*self._args)

        # 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()

if __name__ == '__main__':

    def print_msg(msg):
        print(msg)

    t = KillableThread(10, print_msg, args=("hello world"))
    t.start()
    time.sleep(6)
    print("About to kill thread")
    t.kill()

当然、@ SBCの場合と同様に、スレッドは新しいループが停止して停止するのを待ちません。この例では、スレッドが完了するのをさらに4秒間待つのではなく、「スレッドを終了しようとしています」の直後に「スレッドを強制終了しています」というメッセージが表示されます(すでに6秒間スリープしているため)。

KillableThreadコンストラクタの2番目の引数はカスタム関数です(ここではprint_msg)。Args引数は、ここで関数(( "hello world"))を呼び出すときに使用される引数です。


0

@Kozyarchukの回答で述べたように、トレースのインストールは機能します。この回答にはコードが含まれていなかったため、すぐに使えるすぐに使える例を次に示します。

import sys, threading, time 

class TraceThread(threading.Thread): 
    def __init__(self, *args, **keywords): 
        threading.Thread.__init__(self, *args, **keywords) 
        self.killed = False
    def start(self): 
        self._run = self.run 
        self.run = self.settrace_and_run
        threading.Thread.start(self) 
    def settrace_and_run(self): 
        sys.settrace(self.globaltrace) 
        self._run()
    def globaltrace(self, frame, event, arg): 
        return self.localtrace if event == 'call' else None
    def localtrace(self, frame, event, arg): 
        if self.killed and event == 'line': 
            raise SystemExit() 
        return self.localtrace 

def f(): 
    while True: 
        print('1') 
        time.sleep(2)
        print('2') 
        time.sleep(2)
        print('3') 
        time.sleep(2)

t = TraceThread(target=f) 
t.start() 
time.sleep(2.5) 
t.killed = True

印刷1した後に停止し2ます。3は印刷されません。


-1

プロセスでコマンドを実行してから、プロセスIDを使用してコマンドを強制終了できます。私は2つのスレッド間で同期する必要があり、そのうちの1つはそれ自体では返されません。

processIds = []

def executeRecord(command):
    print(command)

    process = subprocess.Popen(command, stdout=subprocess.PIPE)
    processIds.append(process.pid)
    print(processIds[0])

    #Command that doesn't return by itself
    process.stdout.read().decode("utf-8")
    return;


def recordThread(command, timeOut):

    thread = Thread(target=executeRecord, args=(command,))
    thread.start()
    thread.join(timeOut)

    os.kill(processIds.pop(), signal.SIGINT)

    return;

-1

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

-2

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

方法は次のとおりです。

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()


threading.Threadオブジェクト内でself._Thread__stop()を使用して停止する必要がありました。この例のような単純なself.join()(code.activestate.com/recipes/65448-thread-control-idiom)が機能しない理由がわかりません。
ハリジェイ、

12
「これでスレッドが実際に停止するわけではない」についての詳細が参考になります。
2371年

19
基本的に、_Thread__stopメソッドを呼び出しても、スレッドが停止していることをPythonに伝える以外の効果はありません。実際に実行を継続できます。例については、gist.github.com / 2787191を参照してください。
Bluehorn

35
これは明らかに間違っています。スレッドにstoppedマークする_Thread__stop()だけで実際にはスレッドを停止しません!絶対にしないでください。読んでください
dotancohen 2013

-2

サブタスクを強制終了する機能が本当に必要な場合は、別の実装を使用してください。multiprocessingそして、geventどちらも「スレッド」を無差別に削除することをサポートしています。

Pythonのスレッドはキャンセルをサポートしていません。試しさえしないでください。コードがデッドロックしたり、メモリが破損したりリークしたりする可能性が非常に高くなります。または、まれに非決定的に発生する、デバッグが困難な意図しない「興味深い」効果が発生する可能性があります。


2
…はい、どちらも厳密に「スレッド化」されていないことは知っていますが、コードがモデルに適合する(または適合させることができる)場合、どちらも機能します。
Matthias Urlichs、2015
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.