Можно ли завершить работающий поток без установки / проверки каких-либо флагов / семафоров / и т. Д.?
источник
Можно ли завершить работающий поток без установки / проверки каких-либо флагов / семафоров / и т. Д.?
Как правило, это плохая схема - внезапное завершение потока в 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 Threads Томера Филиба. Цитата о возвращаемом значении, по- PyThreadState_SetAsyncExc
видимому, взята из старой версии Python .)
Как отмечено в документации, это не волшебная палочка, потому что, если поток занят вне интерпретатора Python, он не будет перехватывать прерывание.
Хорошая схема использования этого кода - заставить поток перехватить определенное исключение и выполнить очистку. Таким образом, вы можете прервать задачу и при этом выполнить надлежащую очистку.
SO_REUSEADDR
опцию сокета, чтобы избежатьAddress already in use
ошибки.None
вместо0
дляres != 1
случая, и я должен был позвонитьctypes.c_long(tid)
и передать , что любые ctypes функционировать , а не три раза в день непосредственно.Нет официального API для этого, нет.
Вам нужно использовать API платформы для уничтожения потока, например, pthread_kill или TerminateThread. Вы можете получить доступ к такому API, например, через pythonwin или через ctypes.
Обратите внимание, что это небезопасно. Скорее всего, это приведет к необратимому мусору (из локальных переменных стековых фреймов, которые становятся мусором) и может привести к взаимным блокировкам, если у удаляемого потока есть GIL в момент его завершения.
источник
multiprocessing.Process
может ,p.terminate()
В тех случаях, когда я хочу уничтожить поток, но не хочу использовать флаги / блокировки / сигналы / семафоры / события / что угодно, я продвигаю потоки в полномасштабные процессы. Для кода, который использует только несколько потоков, издержки не так уж и плохи.
Например, это удобно, чтобы легко завершать вспомогательные «потоки», которые выполняют блокирующий ввод / вывод.
Преобразование тривиально: в связанном коде замените все
threading.Thread
наmultiprocessing.Process
и всеqueue.Queue
наmultiprocessing.Queue
и добавьте необходимые вызовыp.terminate()
вашего родительского процесса, который хочет убить своего потомкаp
Смотрите документацию по Python для
multiprocessing
.источник
multiprocessing
это хорошо, но имейте в виду, что аргументы зарезервированы для нового процесса. Так что, если один из аргументов является чем-то непопулярным (например, alogging.log
), его не стоит использоватьmultiprocessing
.multiprocessing
аргументы передаются новому процессу в Windows, но Linux использует разветвление для их копирования (Python 3.7, не знаю, какие другие версии). Таким образом, вы получите код, который работает в Linux, но вызывает ошибки Pickle в Windows.multiprocessing
с ведением журнала это сложное дело. Необходимо использоватьQueueHandler
(см. Этот учебник ). Я научился этому нелегко.Если вы пытаетесь завершить всю программу, вы можете установить поток как «демон». см. Thread.daemon
источник
Как уже упоминали другие, нормой является установка флага остановки. Для чего-то более легкого (без подклассов Thread, без глобальной переменной), лямбда-обратный вызов является опцией. (Обратите внимание на круглые скобки в
if stop()
.)Замена
print()
наpr()
функцию, которая всегда сбрасывает (sys.stdout.flush()
), может улучшить точность вывода оболочки.(Проверено только на Windows / Eclipse / Python3.3)
источник
pr()
функция?Это основано на thread2 - убиваемые темы (рецепт Python)
Вам нужно вызвать PyThreadState_SetasyncExc (), который доступен только через ctypes.
Это было протестировано только на Python 2.7.3, но, вероятно, будет работать с другими недавними выпусками 2.x.
источник
KeyboardInterrupt
возможность очистить их. Если после этого они ВСЕГДА зависнут, тоSystemExit
это уместно, или просто убейте процесс из терминала.pthread_cleanup_push()/_pop()
было бы много работы для правильной реализации, и это заметно замедлило бы работу интерпретатора.Вы никогда не должны принудительно убивать поток, не сотрудничая с ним.
Уничтожение потока снимает любые гарантии того, что блоки try / finally настроены так, что вы можете оставить блокировки заблокированными, открытыми файлами и т. Д.
Единственный раз, когда вы можете утверждать, что принудительное уничтожение потоков - это хорошая идея, это быстрое уничтожение программы, но не отдельных потоков.
источник
В Python вы просто не можете уничтожить поток напрямую.
Если вам НЕ действительно нужен поток (!), То вместо использования пакета потоков вы можете использовать многопроцессорный пакет . Здесь, чтобы убить процесс, вы можете просто вызвать метод:
Python убьет ваш процесс (в Unix через сигнал SIGTERM, а в Windows через
TerminateProcess()
вызов). Обратите внимание, чтобы использовать его при использовании очереди или трубы! (это может повредить данные в очереди / конвейере)Обратите внимание, что
multiprocessing.Event
иmultiprocessing.Semaphore
работают точно так же, какthreading.Event
иthreading.Semaphore
соответственно. Фактически, первые - клоны последних.Если вам ДЕЙСТВИТЕЛЬНО нужно использовать поток, нет способа уничтожить его напрямую. Однако вы можете использовать «поток демона» . На самом деле, в Python поток может быть помечен как демон :
Основная программа завершит работу, когда не останется живых потоков, не являющихся демонами. Другими словами, когда ваш основной поток (который, конечно, не является потоком демона) завершит свои операции, программа завершит работу, даже если все еще работают некоторые потоки демона.
Обратите внимание, что необходимо установить поток как
daemon
перед вызовомstart()
метода!Конечно, вы можете и должны использовать
daemon
даже сmultiprocessing
. Здесь, когда основной процесс завершается, он пытается завершить все свои демонические дочерние процессы.Наконец, обратите внимание, что это
sys.exit()
иos.kill()
не варианты.источник
Вы можете убить поток, установив трассировку в поток, который выйдет из потока. Смотрите прикрепленную ссылку для одной возможной реализации.
Убить нить в Python
источник
Если вы явно вызываете
time.sleep()
как часть своего потока (скажем, опрашиваете какую-то внешнюю службу), то улучшением метода Филиппа является использование тайм-аута в методеevent
's,wait()
где бы вы ни находилисьsleep()
Например:
Затем запустить его
Преимущество использования
wait()
вместоsleep()
проверки и регулярной проверки события состоит в том, что вы можете программировать с более длительными интервалами сна, поток останавливается почти сразу (когда вы в противном случаеsleep()
), и, на мой взгляд, код для обработки выхода значительно проще ,источник
Лучше, если вы не убьете нить. Можно было бы ввести блок try в цикл потока и вызвать исключение, когда вы хотите остановить поток (например, break / return / ..., который останавливает ваш for / while / ...). Я использовал это в своем приложении, и это работает ...
источник
Определенно возможно реализовать
Thread.stop
метод, как показано в следующем примере кода:Thread3
Класс появляется для запуска кода примерно на 33% быстрее , чемThread2
класс.источник
self.__stop
чтобы быть установленным в поток. Обратите внимание, что, как и большинство других решений здесь, он фактически не прерывает блокирующий вызов, поскольку функция трассировки вызывается только при вводе новой локальной области. Также стоит отметить, что онsys.settrace
действительно предназначен для реализации отладчиков, профилей и т. Д. И поэтому считается деталью реализации CPython и не гарантированно существует в других реализациях Python.Thread2
классом заключается в том, что он выполняет код примерно в десять раз медленнее. Некоторые люди все еще могут найти это приемлемым.т твой
Thread
объект.Прочитайте исходный код python (
Modules/threadmodule.c
иPython/thread_pthread.h
), и вы увидите, чтоThread.ident
этоpthread_t
тип, так что вы можете делать все, чтоpthread
можете сделать в использовании pythonlibpthread
.источник
Для обхода потока можно использовать следующий обходной путь:
Это может быть использовано даже для завершения потоков, чей код написан в другом модуле, из основного потока. Мы можем объявить глобальную переменную в этом модуле и использовать ее для завершения потоков, порожденных в этом модуле.
Я обычно использую это для завершения всех потоков при выходе из программы. Это может быть не идеальный способ завершить поток (ы), но может помочь.
источник
Я опаздываю к этой игре, но я боролся с подобным вопросом, и следующее, кажется, как решило проблему для меня идеально, так и позволило мне выполнить некоторую базовую проверку и очистку состояния потока при выходе из демонизированного подпотока:
Урожайность:
источник
SystemExit
вafter_timeout
потоке делает что-либо с основным потоком (который просто ожидает выхода в первом примере в этом примере)?SystemExit
имеет только два специальных свойства: он не производит трассировку (когда любой поток завершается, выбрасывая один), и если основной поток завершается, бросая один, он устанавливает состояние выхода (в то же время ожидая других потоков, не являющихся демонами). выходить).Одна вещь, которую я хочу добавить, это то, что если вы читаете официальную документацию в потоке lib Python , рекомендуется избегать использования «демонических» потоков, когда вы не хотите, чтобы потоки заканчивались внезапно, с флагом, упомянутым Паоло Ровелли .
Из официальной документации:
Я думаю, что создание демонических потоков зависит от вашего приложения, но в целом (и на мой взгляд) лучше не убивать их или делать их демоническими. В многопроцессорной обработке вы можете использовать
is_alive()
для проверки состояния процесса и «завершить» для завершения их (также вы избегаете проблем GIL). Но иногда вы можете найти больше проблем, когда выполняете свой код в Windows.И всегда помните, что если у вас есть «живые потоки», интерпретатор Python будет их ждать. (Из-за этого демон может помочь вам, если не имеет значения, внезапно заканчивается).
источник
Для этого есть библиотека, стопит . Хотя некоторые из тех же предостережений, перечисленных здесь, все еще применяются, по крайней мере, эта библиотека представляет регулярную, повторяемую технику для достижения поставленной цели.
источник
Хотя он довольно старый, для некоторых это может быть удобным решением:
Таким образом, он позволяет «потоку вызывать исключения в контексте другого потока» и, таким образом, завершенный поток может обрабатывать завершение без регулярной проверки флага прерывания.
Однако, согласно исходному коду, есть некоторые проблемы с этим кодом.
источник
Это похоже на работу с pywin32 на Windows 7
источник
Питер Хинтдженс, один из основателей проекта ØMQ, говорит, что использование ØMQ и избегание примитивов синхронизации, таких как блокировки, мьютексы, события и т. Д., Является самым безопасным и безопасным способом написания многопоточных программ:
http://zguide.zeromq.org/py:all#Multithreading-with-ZeroMQ
Это включает в себя сообщение дочернему потоку, что он должен отменить свою работу. Это можно сделать, оснастив поток ØMQ-сокетом и опросив на нем сокет, чтобы получить сообщение о том, что он должен быть отменен.
Ссылка также предоставляет пример многопоточного кода Python с ØMQ.
источник
Предполагая, что вы хотите иметь несколько потоков одной и той же функции, это ИМХО самая простая реализация, чтобы остановить один по id:
Хорошая вещь здесь, вы можете иметь несколько одинаковых и разных функций, и остановить их все
functionname.stop
Если вы хотите иметь только один поток функции, вам не нужно запоминать идентификатор. Просто остановитесь, если
doit.stop
> 0.источник
Просто чтобы развить идею @ SCB (именно это мне и было нужно) создать подкласс KillableThread с настраиваемой функцией:
Естественно, как и в случае с @SBC, поток не ожидает запуска нового цикла для остановки. В этом примере вы увидите сообщение «Killing Thread», напечатанное сразу после «About to kill thread», вместо того, чтобы ждать еще 4 секунды до завершения потока (так как мы проспали уже 6 секунд).
Второй аргумент в конструкторе KillableThread - ваша пользовательская функция (здесь print_msg). Аргумент Args - это аргументы, которые будут использоваться при вызове функции (("hello world")) здесь.
источник
Как уже упоминалось в @ Kozyarchuk в ответ , установка трассировки работает. Поскольку этот ответ не содержал кода, вот рабочий готовый пример:
Останавливается после печати
1
и2
.3
не печатается.источник
Вы можете выполнить свою команду в процессе, а затем уничтожить ее, используя идентификатор процесса. Мне нужно было синхронизировать два потока, один из которых не возвращается сам по себе.
источник
Запустите подпоток с помощью setDaemon (True).
источник
Вот как это сделать:
Дайте ему несколько секунд, после чего ваш поток должен быть остановлен. Проверьте также
thread._Thread__delete()
метод.Я бы порекомендовал
thread.quit()
метод для удобства. Например, если у вас есть сокет в вашем потоке, я бы порекомендовал создатьquit()
метод в вашем классе дескриптора сокета, завершить сокет, а затем запуститьthread._Thread__stop()
внутри вашегоquit()
.источник
_Thread__stop()
просто помечает поток как остановленный , он фактически не останавливает поток! Никогда не делай этого. Прочитайте .Если вам действительно нужна возможность убить подзадачу, используйте альтернативную реализацию.
multiprocessing
иgevent
оба поддерживают без разбора убивая "нить".Поток Python не поддерживает отмену. Даже не пытайся. Скорее всего, ваш код заблокирует, повредит или утечет память, или будет иметь другие непреднамеренные "интересные" трудно отлаживаемые эффекты, которые происходят редко и недетерминированно.
источник