스레드를 죽이는 방법이 있습니까?
플래그 / 세마포어 등을 설정 / 확인하지 않고 실행중인 스레드를 종료 할 수 있습니까?
일반적으로 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'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 )
( Tomer Filiba의 Killable Threads 를 기반으로합니다 .의 반환 값에 대한 인용문은 이전 버전의 PythonPyThreadState_SetAsyncExc
에서 가져온 것 같습니다 .)
문서에서 언급했듯이 스레드가 Python 인터프리터 외부에서 사용 중이면 인터럽트를 포착하지 못하기 때문에 이것은 마법의 총알이 아닙니다.
이 코드의 좋은 사용 패턴은 스레드가 특정 예외를 포착하고 정리를 수행하도록하는 것입니다. 이렇게하면 작업을 중단하고 적절한 정리를 할 수 있습니다.
이를위한 공식 API는 없습니다.
스레드를 죽이려면 플랫폼 API를 사용해야합니다 (예 : pthread_kill 또는 TerminateThread). 예를 들어 pythonwin 또는 ctypes를 통해 이러한 API에 액세스 할 수 있습니다.
이것은 본질적으로 안전하지 않습니다. 그것은 (가비지가되는 스택 프레임의 지역 변수로부터) 수집 할 수없는 쓰레기로 이어질 것이고, 죽는 스레드가 죽을 때 GIL을 가지고 있다면 교착 상태로 이어질 수 있습니다.
multiprocessing.Process
캔p.terminate()
스레드를 죽이고 싶지만 플래그 / 잠금 / 신호 / 세마포어 / 이벤트 / 무엇이든 사용하고 싶지 않은 경우 스레드를 완전한 프로세스로 승격합니다. 몇 개의 스레드 만 사용하는 코드의 경우 오버 헤드가 그렇게 나쁘지 않습니다.
예를 들어 이것은 블로킹 I / O를 실행하는 도우미 "스레드"를 쉽게 종료하는 데 유용합니다.
변환은 간단하다 : 관련 코드에서 모두 교체 threading.Thread
로 multiprocessing.Process
모든 queue.Queue
과를 multiprocessing.Queue
하고 필요한 호출을 추가 p.terminate()
자식을 죽이고 싶어 당신의 부모 프로세스에p
전체 프로그램을 종료하려는 경우 스레드를 "데몬"으로 설정할 수 있습니다. Thread.daemon 참조
이것은 thread2-killable threads (Python 레시피)를 기반으로합니다.
ctypes를 통해서만 사용할 수있는 PyThreadState_SetasyncExc ()를 호출해야합니다.
이것은 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")
협력하지 않고 스레드를 강제로 종료해서는 안됩니다.
스레드를 종료하면 try / finally 차단 설정이 보장되지 않으므로 잠금을 잠그고 파일을 열어 둘 수 있습니다.
스레드를 강제로 죽이는 것이 좋은 생각이라고 주장 할 수있는 유일한 경우는 프로그램을 빨리 죽이는 것입니다.
다른 사람들이 언급했듯이 표준은 중지 플래그를 설정하는 것입니다. 경량 (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에서만 테스트 됨)
파이썬에서는 단순히 쓰레드를 직접 죽일 수 없습니다.
실제로 Thread (!)가 필요하지 않은 경우 threading 패키지 를 사용하는 대신 수행 할 수있는 작업 은 multiprocessing 패키지 를 사용하는 것 입니다. 여기서 프로세스를 종료하려면 메서드를 호출하기 만하면됩니다.
yourProcess.terminate() # kill the process!
Python은 프로세스를 종료합니다 (Unix에서는 SIGTERM 신호를, Windows에서는 TerminateProcess()
호출을 통해 ). 큐나 파이프를 사용하는 동안 사용에주의하세요! (대기열 / 파이프의 데이터가 손상 될 수 있음)
참고 그 multiprocessing.Event
와 multiprocessing.Semaphore
의 같은 방법으로 정확하게 작동 threading.Event
하고, threading.Semaphore
각각은. 사실, 첫 번째 것은 후자의 클론입니다.
정말로 쓰레드를 사용해야한다면 직접 죽일 방법이 없습니다. 그러나 할 수있는 것은 "데몬 스레드" 를 사용하는 것 입니다. 사실, 파이썬에서 스레드는 데몬 으로 표시 될 수 있습니다 .
yourThread.daemon = True # set the Thread as a "daemon thread"
살아있는 비 데몬 스레드가 남아 있지 않으면 메인 프로그램이 종료됩니다. 즉, 메인 스레드 (물론 데몬이 아닌 스레드)가 작업을 완료하면 데몬 스레드가 여전히 작동하고 있어도 프로그램이 종료됩니다.
메서드가 호출 daemon
되기 전에 스레드를 설정 해야합니다 start()
!
물론 당신은 할 수 및 사용해야 daemon
와도 multiprocessing
. 여기서 주 프로세스가 종료되면 모든 데몬 자식 프로세스를 종료하려고합니다.
마지막으로, sys.exit()
및 os.kill()
선택 사항이 아닙니다.
스레드를 종료 할 스레드에 추적을 설치하여 스레드를 종료 할 수 있습니다. 가능한 구현에 대해서는 첨부 된 링크를 참조하십시오.
스레드를 죽이지 않는 것이 좋습니다. 방법은 스레드의주기에 "try"블록을 도입하고 스레드를 중지하려는 경우 예외를 throw하는 것입니다 (예 : for / while / ...을 중지하는 break / return / ...). 나는 이것을 내 앱에서 사용했으며 작동합니다 ...
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
클래스는 Thread2
클래스 보다 약 33 % 빠르게 코드를 실행하는 것으로 보입니다 .
from ctypes import *
pthread = cdll.LoadLibrary("libpthread-2.15.so")
pthread.pthread_cancel(c_ulong(t.ident))
t 는 당신의 Thread
물건입니다.
파이썬 소스를 읽기 ( Modules/threadmodule.c
그리고 Python/thread_pthread.h
당신은 볼 수) Thread.ident
입니다 pthread_t
당신은 아무것도 할 수 있도록 입력 pthread
파이썬 사용 할 수 있습니다 libpthread
.
다음 해결 방법을 사용하여 스레드를 종료 할 수 있습니다.
kill_threads = False
def doSomething():
global kill_threads
while True:
if kill_threads:
thread.exit()
......
......
thread.start_new_thread(doSomething, ())
이것은 메인 스레드에서 다른 모듈에 코드가 작성된 스레드를 종료하는 데에도 사용할 수 있습니다. 해당 모듈에서 전역 변수를 선언하고이를 사용하여 해당 모듈에서 생성 된 스레드를 종료 할 수 있습니다.
나는 보통 이것을 사용하여 프로그램 종료시 모든 스레드를 종료합니다. 이것은 스레드를 종료하는 완벽한 방법은 아니지만 도움이 될 수 있습니다.
명시 적으로 호출하는 경우 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
ing wait()
대신 사용 sleep()
하고 정기적으로 이벤트를 확인하는 이점은 더 긴 수면 간격으로 프로그래밍 할 수 있다는 것입니다. 스레드는 거의 즉시 중지되며 (다른 방법으로 수행 할 때 sleep()
) 제 생각에는 종료 처리 코드가 훨씬 더 간단합니다. .
나는이 게임에 늦었지만 비슷한 질문 으로 씨름하고 있었고 다음은 모두 문제를 완벽하게 해결하는 것으로 보이며 데몬 화 된 하위 스레드가 종료되면 기본적인 스레드 상태 검사 및 정리를 수행 할 수 있습니다.
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]
내가 추가하고 싶은 것은 lib Python 에서 공식 문서를 읽는다면 Paolo Rovelli가 언급 한 플래그와 함께 쓰레드가 갑자기 끝나는 것을 원하지 않을 때 "악마"쓰레드를 사용하지 않는 것이 좋습니다 .
공식 문서에서 :
데몬 스레드는 종료시 갑자기 중지됩니다. 해당 리소스 (예 : 열린 파일, 데이터베이스 트랜잭션 등)가 제대로 해제되지 않을 수 있습니다. 스레드가 정상적으로 중지되도록하려면 스레드를 데모닉이 아닌 것으로 만들고 이벤트와 같은 적절한 신호 메커니즘을 사용하십시오.
데몬 스레드를 만드는 것은 응용 프로그램에 따라 다르지만 일반적으로 (내 의견으로는) 그들을 죽이거나 데몬으로 만드는 것을 피하는 것이 좋습니다. 다중 처리에서는 is_alive()
프로세스 상태를 확인하고 "종료"하여 완료 할 수 있습니다 (또한 GIL 문제를 방지 할 수 있음). 그러나 때때로 Windows에서 코드를 실행할 때 더 많은 문제를 찾을 수 있습니다.
그리고 "라이브 스레드"가 있으면 Python 인터프리터가 실행될 때까지 기다릴 것임을 항상 기억하십시오. (이 데몬 때문에 갑작스런 종료가 중요하지 않으면 도움이 될 수 있습니다).
다소 오래되었지만 다음과 같은 경우에 편리한 솔루션 이 될 수 있습니다.
스레딩의 모듈 기능을 확장하는 작은 모듈-한 스레드가 다른 스레드의 컨텍스트에서 예외를 발생시킬 수 있습니다. 를 올리면
SystemExit
마침내 파이썬 스레드를 죽일 수 있습니다.
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 코드로 반환 될 때만 예외가 발생합니다.
- 내장 함수가 내부적으로 PyErr_Clear ()를 호출하는 경우에도 문제가 발생하여 보류중인 예외를 효과적으로 취소 할 수 있습니다. 다시 올릴 수 있습니다.
- 예외 유형 만 안전하게 발생할 수 있습니다. 예외 인스턴스는 예상치 못한 동작을 유발할 수 있으므로 제한됩니다.
- 예 : t1.raise_exc (TypeError)가 아니라 t1.raise_exc (TypeError ( "blah")).
- IMHO 그것은 버그이고 나는 그것을 하나로보고했습니다. 자세한 정보는 http://mail.python.org/pipermail/python-dev/2006-August/068158.html
- 이 함수를 내장 스레드 모듈에 노출하도록 요청했지만 ctypes는 표준 라이브러리 (2.5 기준)가되었고이
기능은 구현에 구애받지 않을 가능성이 높지 않으므로
노출되지 않은 상태로 유지 될 수 있습니다 .
이를 위해 구축 된 라이브러리 stopit이 있습니다. 여기에 나열된 동일한주의 사항 중 일부가 여전히 적용되지만 적어도이 라이브러리는 명시된 목표를 달성하기위한 규칙적이고 반복 가능한 기술을 제공합니다.
이것은 Windows 7에서 pywin32와 함께 작동하는 것 같습니다.
my_thread = threading.Thread()
my_thread.start()
my_thread._Thread__stop()
이것은 잘못된 대답입니다. 의견을 참조하십시오.
방법은 다음과 같습니다.
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()
.
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
ØMQ 프로젝트 의 창립자 중 한 명인 Pieter Hintjens는 ØMQ를 사용하고 잠금, 뮤텍스, 이벤트 등과 같은 동기화 기본 요소를 피하는 것이 멀티 스레드 프로그램을 작성하는 가장 안전하고 안전한 방법이라고 말합니다.
http://zguide.zeromq.org/py:all#Multithreading-with-ZeroMQ
여기에는 자식 스레드에게 작업을 취소해야한다고 알리는 것도 포함됩니다. 이것은 스레드에 ØMQ 소켓을 장착하고 해당 소켓에서 취소해야한다는 메시지를 폴링하여 수행됩니다.
링크는 ØMQ를 사용하는 다중 스레드 파이썬 코드에 대한 예제도 제공합니다.
동일한 기능의 여러 스레드를 원한다고 가정하면 IMHO는 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
함수의 스레드가 하나만 있으면 ID를 기억할 필요가 없습니다. doit.stop
> 0 이면 중지하십시오 .
프로세스에서 명령을 실행 한 다음 프로세스 ID를 사용하여 명령을 종료 할 수 있습니다. 나는 그 중 하나가 자체적으로 반환되지 않는 두 스레드 사이를 동기화해야했습니다.
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의 스레딩은 취소를 지원하지 않습니다. 시도하지도 마라. 귀하의 코드는 교착 상태, 손상 또는 메모리 누수 가능성이 매우 높거나 드물고 비 결정적으로 발생하는 의도하지 않은 "흥미로운"디버깅하기 어려운 효과가 있습니다.
참고 URL : https://stackoverflow.com/questions/323972/is-there-any-way-to-kill-a-thread
'development' 카테고리의 다른 글
Java에서 파일을 byte []로 (0) | 2020.09.30 |
---|---|
Java / Maven에서 "Xerces hell"을 다루고 있습니까? (0) | 2020.09.30 |
검색 엔진은 AngularJS 애플리케이션을 어떻게 처리합니까? (0) | 2020.09.30 |
PHP를 사용하여 두 날짜의 차이를 계산하는 방법은 무엇입니까? (0) | 2020.09.30 |
git 저장소의 줄 수 계산 (0) | 2020.09.30 |