Есть ли способ убить поток в Python?

можно ли завершить текущий поток без установки/проверки каких-либо флагов/семафоров / и т. д.?

24 ответов


это, как правило, плохой шаблон, чтобы убить поток резко, на 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 (). Поток должен проверять флаг stop через регулярные промежутки времени.

однако бывают случаи, когда вам действительно нужно убить поток. Например, когда вы обертываете внешнюю библиотеку, которая занята для длительных вызовов, и вы хотите прервать его.

следующий код позволяет (с некоторыми ограничениями) вызывать исключение в потоке 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 )

(исходя из Темы Навахе Томер Филиба. Цитата о возвращаемом значении PyThreadState_SetAsyncExc видимого старая версия Python.)

как отмечено в документации, это не волшебная пуля, потому что если поток занят вне интерпретатора Python, он не поймает перерыв.

хороший шаблон использования этого кода состоит в том, чтобы поток поймал определенное исключение и выполнил очистку. Таким образом, вы можете прервать задачу и все еще иметь правильную очистку.


для этого нет официального API, нет.

вам нужно использовать API платформы, чтобы убить поток, например pthread_kill или TerminateThread. Вы можете получить доступ к такому API, например, через pythonwin или через ctypes.

обратите внимание, что это по своей сути небезопасно. Это, вероятно, приведет к необнаруживаемому мусору (из локальных переменных кадров стека, которые становятся мусором), и может привести к взаимоблокировкам, если поток, который убивается, имеет GIL в точке, когда он убит.


A multiprocessing.Process can p.terminate()

в случаях, когда я хочу убить поток, но не хочу использовать флаги/блокировки/сигналы/семафоры/события/что угодно, я продвигаю потоки к полномасштабным процессам. Для кода, который использует несколько потоков накладные расходы не так уж плохо.

например. это удобно, чтобы легко прекратить вспомогательные "потоки", которые выполняют блокировку ввода-вывода

преобразование тривиально: в соответствующем коде замените все threading.Thread С multiprocessing.Process и все queue.Queue С multiprocessing.Queue и добавьте необходимые вызовы p.terminate() к вашему родительскому процессу, который хочет убить своего ребенка p

Python doc


Если вы пытаетесь завершить всю программу, вы можете установить поток как "демон". видеть нить.демон


вы никогда не должны насильственно убивать нить, не сотрудничая с ней.

убийство потока удаляет все гарантии, что try / finally блокирует настройку, чтобы вы могли оставить блокировки заблокированными, файлы открытыми и т. д.

единственный раз, когда вы можете утверждать, что насильственное убийство потоков-хорошая идея-убить программу быстро, но никогда не отдельные потоки.


это основано на thread2 -- killable threads (рецепт Python)

вам нужно вызвать PyThreadState_SetasyncExc (), который доступен только через ctypes.

Это было протестировано только на Python 2.7.3, но, вероятно, будет работать с другими недавними 2.х выпусках.

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

как упоминали другие, нормой является установка флага остановки. Для чего-то легкого (без подклассов потока, без глобальной переменной), лямбда-обратный вызов является опцией. (Обратите внимание на скобки в 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)


в Python, вы просто не можете убить поток напрямую.

Если вам действительно не нужно иметь поток (!), что вы можете сделать, вместо использования резьбонарезной пакета , использовать многопроцессорность пакета . Здесь, чтобы убить процесс, вы можете просто вызвать метод:

yourProcess.terminate()  # kill the process!

Python убьет ваш процесс (в Unix через сигнал SIGTERM, а в Windows через TerminateProcess() призывать.) Обратите внимание, чтобы использовать его при использовании очереди или трубы! (это может повредить данные в очереди/канале)

отметим, что multiprocessing.Event и multiprocessing.Semaphore работа точно таким же образом threading.Event и threading.Semaphore соответственно. На самом деле, первые клоны последних.

Если вам действительно нужно использовать поток, нет никакого способа убить его напрямую. Однако вы можете использовать "поток демона". Фактически, в Python поток может быть помечено как демон:

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

основная программа выйдет, когда не останется живых потоков не-демона. Другими словами, когда ваш основной поток (который, конечно же, не является потоком демона) завершит свои операции, программа выйдет, даже если некоторые потоки демона все еще работают.

обратите внимание, что необходимо установить поток как daemon до start() метод называется!

конечно, вы можете и должны использовать daemon даже с multiprocessing. Здесь, когда основной процесс завершается, он пытается завершить все свои демонические дочерние процессы.

наконец, обратите внимание, что sys.exit() и os.kill() не выбор.


вы можете убить поток, установив трассировку в поток, который выйдет из потока. См. прилагаемую ссылку для одной возможной реализации.

убить поток в Python


лучше, если вы не убиваете нить. Одним из способов может быть введение блока "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 __name__ == '__main__':
    main()

на Thread3 класс, похоже, запускает код примерно на 33% быстрее, чем Thread2 класса.


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

t ваш


одна вещь, которую я хочу добавить, это то, что если Вы читаете официальную документацию в threading lib Python, рекомендуется избегать использования" демонических " потоков, когда вы не хотите, чтобы потоки заканчивались резко, с флагом, который Paolo Rovelli указано.

из официальной документации:

потоки демона резко останавливаются при завершении работы. Их ресурсы (например, открытые файлы, транзакции базы данных и т. д.) не может быть освобожден должным образом. Если вы хотите, чтобы ваши потоки, чтобы изящно остановиться, сделать их не-демонических и использовать подходящий механизм сигнализации, таких как событие.

Я думаю, что создание демонических потоков зависит от вашего приложения, но в целом (и на мой взгляд) лучше не убивать их или делая их демоническими. В многопроцессорной обработке вы можете использовать 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]

это плохой ответ см. в комментарии

вот как это сделать:

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() метод в вашем классе socket-handle, завершите сокет, затем запустите thread._Thread__stop() внутри quit().


запустите вспомогательный поток с помощью 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

а вот это, этой может быть удобным решением для некоторых:

маленький модуль, который расширяет функциональность модуля резьбы по -- позволяет одному потоку создавать исключения в контексте другого нитка. Поднимая 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 (), который эффективно отменит ожидающее исключение. Вы можете попытаться поднять его снова.
  • только типы исключений могут быть подняты безопасно. Экземпляры исключений могут вызвать непредвиденное поведение и поэтому ограничены.
  • я попросил выставить эту функцию во встроенном модуле thread, но так как ctypes стал стандартной библиотекой (начиная с 2.5), и это
    функция вряд ли будет реализована-агностик, ее можно сохранить
    неэкспонированный.

Питер Хинтьенс - один из основателей ØMQ - project -- говорит, используя ØMQ и избегая примитивов синхронизации, таких как блокировки, мьютексы, события и т. д., является самым разумным и безопасным способом написания многопоточных программ:

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

Это включает в себя указание дочернему потоку, что он должен отменить свою работу. Это было бы сделано путем оборудовать поток с ØMQ-гнездом и опрашивать дальше этот сокет для сообщения о том, что он должен отменить.

ссылка также предоставляет пример многопоточного кода python с ØMQ.


для уничтожения потока можно использовать следующий обходной путь:

kill_threads = False

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

thread.start_new_thread(doSomething, ())

Это можно использовать даже для завершения потоков, код которых написан в другом модуле, из основного потока. Мы можем объявить глобальную переменную в этом модуле и использовать ее для завершения потока/s, порожденного в этом модуле.

обычно я использую это для завершения всех потоков при выходе из программы. Это может быть не идеальный способ завершить поток / С, но может помочь.


если вы явно вызываете time.sleep() как часть вашего потока (скажем, опрос некоторой внешней службы), улучшение метода Филиппа заключается в использовании тайм-аута в event ' s 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) и, на мой взгляд, код для обработки выхода значительно проще.


для этой цели построена библиотека,остановить ее. Хотя некоторые из перечисленных здесь предостережений все еще применимы, по крайней мере, эта библиотека представляет собой регулярную, повторяющуюся технику для достижения заявленной цели.


это, кажется, работает с pywin32 в windows 7

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

вы можете выполнить свою команду в процессе, а затем убить ее, используя идентификатор процесса. Мне нужно было синхронизировать между двумя потоками, один из которых не возвращается сам по себе.

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;

Если вам действительно нужна возможность убить подзадачу, используйте альтернативную реализацию. multiprocessing и gevent оба поддерживают без разбора убийство "потока".

поток Python не поддерживает отмену. Даже не пытайся. Ваш код, скорее всего, блокируется, повреждается или утечка памяти или имеет другие непреднамеренные "интересные" труднодоступные эффекты, которые происходят редко и недетерминированно.