Возможно ли завершить рабочий поток без установки/проверки любых флагов/семафоров/и т.д.?
Есть ли способ убить нить?
Ответ 1
Как правило, это плохая схема - внезапное завершение потока в 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 a simpler way to do : self.ident
raise AssertionError("could not determine the thread 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 Threads Томера Филиба. Похоже, цитата о возвращаемом значении PyThreadState_SetAsyncExc
взята из старой версии Python.)
Как отмечено в документации, это не волшебная палочка, потому что, если поток занят вне интерпретатора Python, он не поймает прерывание.
Хорошая схема использования этого кода - заставить поток перехватить определенное исключение и выполнить очистку. Таким образом, вы можете прервать задачу и при этом выполнить надлежащую очистку.
Ответ 2
Для этого нет официального API, нет.
Вам нужно использовать API платформы, чтобы убить поток, например. pthread_kill или TerminateThread. Вы можете получить доступ к этому API, например. через pythonwin или через ctypes.
Обратите внимание, что это по своей сути небезопасно. Вероятно, это приведет к бесполезному мусору (из локальных переменных фреймов стека, которые становятся мусором) и может привести к взаимоблокировкам, если убитый поток имеет GIL в момент, когда он был убит.
Ответ 3
A multiprocessing.Process
может p.terminate()
В тех случаях, когда я хочу убить поток, но не хочу использовать флаги/блокировки/сигналы/семафоры/события/что-то еще, я продвигаю потоки к полномасштабным процессам. Для кода, который использует только несколько потоков, накладные расходы не так уж плохи.
например. это очень удобно, чтобы легко прекратить вспомогательные "потоки", которые выполняют блокировку ввода/вывода
Преобразование тривиально: в связанном коде замените все threading.Thread
на multiprocessing.Process
и все queue.Queue
на multiprocessing.Queue
и добавьте требуемые вызовы p.terminate()
к вашему родительскому процессу, который хочет убить его дочерний элемент p
Ответ 4
Если вы пытаетесь завершить всю программу, вы можете установить поток в качестве "демона". видеть Thread.daemon
Ответ 5
Это основано на thread2 - killable threads (рецепт 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")
Ответ 6
Вы никогда не должны насильно убивать нить, не сотрудничая с ней.
Убийство нити устраняет любые гарантии, которые пытаются установить/окончательно блокировать, чтобы вы могли заблокировать блокировки, открыть файлы и т.д.
Единственный раз, когда вы можете утверждать, что принудительное уничтожение потоков - хорошая идея - быстро убить программу, но никогда не создавать нити.
Ответ 7
Как отмечали другие, нормой является установка стоп-флага. Для чего-то легкого (без подкласса 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 __name__ == '__main__':
main()
Замена print()
с помощью функции pr()
, которая всегда сбрасывается (sys.stdout.flush()
), может улучшить точность вывода оболочки.
(тестируется только на Windows/Eclipse/Python3.3)
Ответ 8
В Python вы просто не можете напрямую убить поток.
Если вам действительно не нужен поток (!), То вместо использования пакета потоковой обработки вы можете использовать пакет многопроцессорности. Здесь, чтобы убить процесс, вы можете просто вызвать метод:
yourProcess.terminate() # kill the process!
Python убьет ваш процесс (в Unix через сигнал SIGTERM, в то время как в Windows через вызов TerminateProcess()
). Обратите внимание, чтобы использовать его при использовании очереди или трубы! (это может повредить данные в очереди/трубке)
Обратите внимание, что multiprocessing.Event
и multiprocessing.Semaphore
multiprocessing.Event
- multiprocessing.Semaphore
работают точно так же, как и threading.Event
и threading.Semaphore
соответственно. Фактически, первые являются клонами последних.
Если вам действительно нужно использовать Thread, невозможно напрямую его убить. Однако вы можете использовать "поток демона". Фактически, в Python поток может быть помечен как демон:
yourThread.daemon = True # set the Thread as a "daemon thread"
Основная программа выйдет, если не осталось живых ничейных нитей. Другими словами, когда ваш основной поток (который, конечно же, не-демонный поток) завершит свои операции, программа выйдет, даже если есть еще некоторые потоки демона.
Обратите внимание, что перед start()
метода start()
необходимо установить Thread как daemon
!
Конечно, вы можете и должны использовать daemon
даже при multiprocessing
. Здесь, когда основной процесс завершается, он пытается завершить все свои демонические дочерние процессы.
Наконец, пожалуйста, обратите внимание, что sys.exit()
и os.kill()
не являются выборами.
Ответ 9
Вы можете убить поток, установив трассировку в поток, который выйдет из потока. См. Прикрепленную ссылку для одной возможной реализации.
Ответ 10
Лучше, если вы не убили нить. Способ может состоять в том, чтобы внедрить блок "try" в цикл потоков и выбросить исключение, когда вы хотите остановить поток (например, break/return/..., который останавливает вашу запись /while/...). Я использовал это в своем приложении, и он работает...
Ответ 11
Определенно возможно реализовать метод 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
.
Ответ 12
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 use libpthread
.
Ответ 13
Следующее обходное решение может быть использовано для уничтожения потока:
kill_threads = False
def doSomething():
global kill_threads
while True:
if kill_threads:
thread.exit()
......
......
thread.start_new_thread(doSomething, ())
Это может использоваться даже для завершающих потоков, код которых написан в другом модуле, из основного потока. Мы можем объявить глобальную переменную в этом модуле и использовать ее для прекращения потоков /s, порожденных в этом модуле.
Обычно я использую это для завершения всех потоков при выходе из программы. Возможно, это не идеальный способ прекратить поток /s, но может помочь.
Ответ 14
Если вы явно вызываете time.sleep()
как часть вашего потока (скажем, опрос какой-либо внешней службы), улучшение по методу Phillipe заключается в использовании тайм-аута в методе 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) и на мой взгляд, код для обработки выхода значительно проще.
Ответ 15
Я опоздал на эту игру, но я боролся с подобным вопросом, и мне кажется, что для меня все это прекрасно решает проблему. И позволяет мне выполнять некоторые основные проверки состояния потока и очищать их при выходе из демонаризованного подпотока:
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]
Ответ 16
Одна вещь, которую я хочу добавить, заключается в том, что если вы читаете официальную документацию в threading lib Python, рекомендуется избегать использования "демонических" потоки, когда вы не хотите, чтобы потоки заканчивались внезапно, с флагом, который упоминал Паоло Ровелли .
Из официальной документации:
Потоки Daemon внезапно останавливаются при выключении. Их ресурсы (такие как открытые файлы, транзакции базы данных и т.д.) Могут быть недоступны. Если вы хотите, чтобы ваши потоки прекратились изящно, сделайте их не-демоническими и используйте подходящий механизм сигнализации, такой как событие.
Я думаю, что создание демонических потоков зависит от вашего приложения, но в целом (и, на мой взгляд) лучше избегать их уничтожения или сделать их демоническими. При многопроцессорной обработке вы можете использовать is_alive()
для проверки состояния процесса и "завершения" для их завершения (также вы избегаете проблем GIL). Но вы можете найти больше проблем, иногда, когда вы выполняете свой код в Windows.
И всегда помните, что если у вас есть "живые потоки", интерпретатор Python будет работать для их ожидания. (Из-за этого демоника может помочь вам, если не имеет значения, резко заканчивается).
Ответ 17
Хотя он довольно старый, this может быть удобным решением для некоторых:
Небольшой модуль, расширяющий функциональность модуля потокового модуля - позволяет одному потоку поднимать исключения в контексте другого нить. Подняв
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), и это функция вряд ли будет реализована - агностическая, ее можно сохранить неразоблаченным.
Ответ 18
Для этой цели создана библиотека, остановка. Хотя некоторые из тех же предостережений, перечисленных здесь, все еще применяются, по крайней мере, эта библиотека представляет собой регулярный, повторяемый метод достижения поставленной цели.
Ответ 19
Это плохой ответ, см. комментарии
Вот как это сделать:
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()
.
Ответ 20
Это похоже на работу с pywin32 на windows 7
my_thread = threading.Thread()
my_thread.start()
my_thread._Thread__stop()
Ответ 21
Запустите дополнительный поток с помощью 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
Ответ 22
Pieter Hintjens - один из основателей ØMQ -project - говорит, используя ØMQ и избегая примитивов синхронизации, таких как блокировки, мьютексы, события и т.д., является самым безопасным и безопасным способом написания многопоточных программ:
http://zguide.zeromq.org/py:all#Multithreading-with-ZeroMQ
Это включает в себя указание дочернего потока, что он должен отменить свою работу. Это можно было бы сделать, установив поток с помощью сокета ØMQ и опроса на этом сокете для сообщения о том, что он должен отменить.
В ссылке также приведен пример многопоточного кода на Python с помощью ØMQ.
Ответ 23
Предполагая, что вы хотите иметь несколько потоков одной и той же функции, это ИМХО самая простая реализация, чтобы остановить один по id:
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
Если вы хотите иметь только один поток функции, вам не нужно запоминать идентификатор. Просто остановитесь, если doit.stop
> 0.
Ответ 24
Я думаю, что это довольно опрятное решение: http://code.activestate.com/recipes/496960-thread2-killable-threads/
Он позволяет "потоку создавать исключения в контексте другого потока". Таким образом, завершенный поток может обрабатывать завершение без регулярной проверки флага аббреты.
Ответ 25
Вы можете выполнить свою команду в процессе, а затем убить ее с помощью идентификатора процесса. Мне нужно было синхронизировать между двумя потоками, один из которых не возвращает сам по себе.
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;
Ответ 26
Если вам действительно нужна возможность убить подзадачу, используйте альтернативную реализацию. multiprocessing
и gevent
обе поддерживают без разбора "поток".
Python threading не поддерживает отмену. Даже не пытайся. Ваш код очень вероятен для блокировки, повреждения или утечки памяти или других непреднамеренных "интересных" трудно отладочных эффектов, которые происходят редко и недетерминированно.