Есть ли способ убить нить? - PullRequest
666 голосов
/ 27 ноября 2008

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

Ответы [ 24 ]

8 голосов
/ 04 сентября 2014

Определенно возможно реализовать метод 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.

6 голосов
/ 22 апреля 2013
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.

6 голосов
/ 15 марта 2018

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

kill_threads = False

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

thread.start_new_thread(doSomething, ())

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

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

4 голосов
/ 29 мая 2014

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

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

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

Я думаю, что создание демонических потоков зависит от вашего приложения, но в целом (и на мой взгляд) лучше не убивать их и не делать их демоническими. В многопроцессорной обработке вы можете использовать is_alive(), чтобы проверить состояние процесса и «завершить», чтобы завершить их (также вы избежите проблем с GIL). Но иногда вы можете встретить больше проблем, когда выполняете свой код в Windows.

И всегда помните, что если у вас есть «живые потоки», интерпретатор Python будет их ждать. (Из-за этого демон может помочь вам, если не имеет значения, внезапно заканчивается).

4 голосов
/ 22 мая 2018

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

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]
3 голосов
/ 17 апреля 2018

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

1 голос
/ 24 июля 2018

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

0 голосов
/ 28 апреля 2011

Это плохой ответ, см. Комментарии

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

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 голосов
/ 22 июня 2014

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

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

0 голосов
/ 29 июля 2015

Это похоже на работу с pywin32 на windows 7

my_thread = threading.Thread()
my_thread.start()
my_thread._Thread__stop()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...