스레드를 죽일 방법이 있습니까?


답변:


672

파이썬과 다른 언어로 쓰레드를 갑자기 죽이는 것은 일반적으로 나쁜 패턴입니다. 다음과 같은 경우를 생각해보십시오.

  • 스레드가 올바르게 닫아야하는 중요한 자원을 보유하고 있습니다.
  • 스레드가 종료해야하는 몇 가지 다른 스레드를 작성했습니다.

여유를 가질 수있는 경우 (자신의 스레드를 관리하는 경우)이를 처리하는 좋은 방법은 각 스레드가 규칙적인 간격으로 검사하여 종료 시간인지 확인하는 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(). 스레드는 정기적으로 정지 플래그를 확인해야합니다.

그러나 실제로 스레드를 죽여야 할 경우가 있습니다. 예를 들어 통화량이 많은 외부 라이브러리를 래핑하고 중단하려는 경우가 있습니다.

다음 코드는 파이썬 스레드에서 예외를 발생시킬 수 있습니다.

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 인터프리터 외부에서 사용 중이면 중단을 포착하지 않기 때문에 이것은 마법의 총알이 아닙니다.

이 코드의 좋은 사용 패턴은 스레드가 특정 예외를 포착하고 정리를 수행하도록하는 것입니다. 이렇게하면 작업을 중단하고 여전히 적절한 정리를 수행 할 수 있습니다.


78
@ Bluebird75 : 또한 스레드가 제대로 종료되어야하는 중요한 리소스를 보유하고있을 수 있기 때문에 스레드가 갑자기 종료되지 않아야한다는 주장을 확신하지 못합니다. 메인 프로그램 및 기본 프로그램에서도 마찬가지입니다. 사용자가 갑자기 죽일 수 있습니다 (예를 들어 Unix의 경우 Ctrl-C).이 경우 가능한 한 잘 처리하려고 시도합니다. 따라서 스레드와 관련하여 특별한 점과 왜 주 프로그램과 동일한 치료를받지 않아야하는지 (즉, 갑자기 죽일 수 있는지) 알 수 없습니다. :) 이것에 대해 자세히 설명해 주시겠습니까?
Eric O Lebigot

18
@EOL : 반면에, 스레드가 소유하고있는 모든 리소스가 로컬 리소스 (열린 파일, 소켓) 인 경우 Linux는 프로세스 정리에 상당히 적합하며 누출되지 않습니다. 소켓을 사용하여 서버를 만든 경우가 있었지만 Ctrl-C로 잔인한 중단을 수행하면 소켓을 바인딩 할 수 없기 때문에 더 이상 프로그램을 시작할 수 없습니다. 5 분 기다려야합니다. 적절한 해결책은 Ctrl-C를 잡고 소켓 분리를 깨끗하게하는 것입니다.
Philippe F

10
@ Bluebird75 : btw. SO_REUSEADDR소켓 옵션을 사용 하여 Address already in use오류 를 피할 수 있습니다 .
메사

12
이 답변에 대한 참고 : 나 (py2.6)에 적어도, 나는 통과했다 None대신 0에 대한 res != 1경우, 나는 전화를했다 ctypes.c_long(tid)하고하는 ctypes 직접 TID보다는 기능에 있음을 전달합니다.
월트 W

21
_stop은 이미 Python 3 스레딩 라이브러리에서 사용 중입니다. 따라서 다른 변수를 사용하면 오류가 발생합니다.
사망

113

그렇게 할 공식 API는 없습니다.

pthread_kill 또는 TerminateThread와 같이 스레드를 종료하려면 플랫폼 API를 사용해야합니다. pythonwin 또는 ctypes를 통해 이러한 API에 액세스 할 수 있습니다.

이것은 본질적으로 안전하지 않습니다. 가비지가되는 스택 프레임의 로컬 변수에서 수집 할 수없는 가비지가 발생할 수 있으며, 죽이는 스레드가 죽일 때 GIL이있는 경우 교착 상태가 발생할 수 있습니다.


26
그것은 것입니다 문제의 스레드가 GIL를 보유하고있는 경우 교착 상태로 이어집니다.
Matthias Urlichs

96

multiprocessing.Processp.terminate()

스레드를 죽이고 싶지만 플래그 / 잠금 / 신호 / 세마포어 / 이벤트 / 무엇이든 사용하지 않으려는 경우 스레드를 완전히 처리 된 프로세스로 승격시킵니다. 몇 개의 스레드 만 사용하는 코드의 경우 오버 헤드가 그렇게 나쁘지 않습니다.

예를 들어 이것은 차단 I / O를 실행하는 도우미 "스레드"를 쉽게 종료하는 데 편리합니다.

변환은 간단하다 : 관련 코드를 모두 교체에서 threading.Threadmultiprocessing.Process모든 queue.Queue과를 multiprocessing.Queue하고 필요한 통화를 추가p.terminate() 자식을 죽이고 싶어 당신의 부모 프로세스에p

에 대한 Python 설명서를multiprocessing 참조하십시오 .


감사. 나는 multiprocessing.JoinableQueue로 queue.Queue을 대체하고이 답변 다음 : stackoverflow.com/a/11984760/911207
데이비드 브라운

이 문제에 대한 많은 페이지. 이것은 내가 생각하는 많은 사람들에게 명백한 해결책으로 보인다
지리학

6
multiprocessing멋지지만 새로운 프로세스에 대한 논쟁이 절뚝 거리고 있음을 명심하십시오. 따라서 인수 중 하나가 선택 불가능한 것 (예 : a logging.log) 인 경우 사용하지 않는 것이 좋습니다 multiprocessing.
Lyager

1
multiprocessing인수는 Windows에서 새 프로세스로 선택되지만 Linux는 포크를 사용하여 복사합니다 (Python 3.7, 다른 버전은 확실하지 않음). 따라서 Linux에서는 작동하지만 Windows에서는 피클 오류가 발생하는 코드가 생깁니다.
nyanpasu64

multiprocessing로깅은 까다로운 사업입니다. 사용해야합니다 QueueHandler( 이 학습서 참조 ). 나는 그것을 힘들게 배웠다.
Fanchen Bao

74

전체 프로그램을 종료하려는 경우 스레드를 "데몬"으로 설정할 수 있습니다. Thread.daemon을 참조하십시오 .


이것은 말이되지 않습니다. 설명서에는 "start ()를 호출하기 전에 설정해야하며, 그렇지 않으면 RuntimeError가 발생합니다."라고 명시되어 있습니다. 따라서 원래 데몬이 아닌 스레드를 죽이려면 어떻게 사용할 수 있습니까?
Raffi Khatchadourian

27
Raffi 나는 주 스레드가 종료되면 데몬 스레드가 종료되기를 원한다는 것을 알고 미리 설정 할 것을 제안한다고 생각합니다.
환상적인

1
왜 이것이 받아 들여지지 않는지 잘 모르겠습니다
eric

메인 프로그램이 종료 되어도 스레드가 계속 실행되도록하려는 경우 스레드를 데몬으로 설정하지 않습니까?
Michele Piccolini

당신은 오늘의 내 영웅입니다. 정확히 내가 찾던 것과 추가 할 소란이 전혀 없습니다.
블 리즈

42

다른 사람들이 언급했듯이, 표준은 정지 플래그를 설정하는 것입니다. 가벼운 것 (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에서만 테스트 됨)


1
Linux / Python 2.7에서 확인되었으며 매력처럼 작동합니다. 이것은 공식적인 대답이어야하며 훨씬 간단합니다.
Paul Kenjora

1
Linux Ubuntu Server 17.10 / Python 3.6.3에서 확인되어 작동합니다.
Marcos

1
2.7에서도 확인되었습니다. 좋은 답변입니다!
silgon

pr()기능 이란 무엇입니까 ?
고문

35

이것은 thread2-killable thread (Python recipe)를 기반으로합니다.

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

스레드 KeyboardInterrupt를 정리할 수있는 스레드를 제공하기 위해 이와 같은 것을 사용 하고 있습니다. 그 후에도 여전히 멈춰 있으면 SystemExit적절하거나 터미널에서 프로세스를 종료하십시오.
drevicko

스레드가 현재 실행중인 경우 작동합니다. 스레드가 syscall에 있으면 작동하지 않습니다. 예외는 자동으로 무시됩니다.
Matthias Urlichs

@MatthiasUrlichs 스레드 실행 상태가 무엇인지 감지하고 경고를 인쇄하거나 다시 시도 할 수있는 방법을 알고 있습니까?
Johan Dahlin

1
@JohanDahlin 조금 기다렸다가 다시 시도하려면 isAlive () 테스트를 수행해야합니다. 어쨌든 이것이 효과가 있지만, 나는 그것이 매달려있는 참조를 남기지 않는다는 것을 보증하지는 않습니다. 이론적으로 CPython에서 스레드 킬링을 안전하게 만드는 것이 가능하지만을 신중하게 사용하면 pthread_cleanup_push()/_pop()올바르게 구현하는 데 많은 작업이 필요하며 해석기가 눈에 띄게 느려질 수 있습니다.
Matthias Urlichs

32

협력하지 않고 스레드를 강제로 종료해서는 안됩니다.

스레드를 종료하면 시도 / 마지막으로 설정된 블록을 차단하여 잠금을 잠 그거나 파일을 열어 두는 등의 보장이 제거됩니다.

스레드를 강제로 종료하는 것이 좋은 아이디어라고 주장 할 수있는 유일한 시간은 프로그램을 빠르게 종료하지만 단일 스레드는 사용하지 않는 것입니다.


11
스레드를 말하기가 왜 그렇게 어려운가? 현재 루프를 마칠 때 스스로를 죽이십시오 ... 나는 그것을 얻지 못했습니다.
Mehdi

2
CPU에 내장 된 "루프"를 식별 할 수있는 메커니즘은 없습니다. 현재 루프 안에있는 코드가 종료되면 확인하는 신호를 사용하는 것이 가장 좋습니다. 스레드 동기화를 처리하는 올바른 방법은 협력 수단, 스레드 일시 중단, 재개 및 종료는 응용 프로그램 코드가 아닌 디버거 및 운영 체제를위한 기능입니다.
Lasse V. Karlsen

2
@Mehdi : (개인적으로) 스레드에서 코드를 작성하는 경우 예, 동의합니다. 그러나 타사 라이브러리를 실행하는 경우가 있으며 해당 코드의 실행 루프에 액세스 할 수 없습니다. 요청 된 기능에 대한 하나의 사용 사례입니다.
Dan H

@DanH 어떤 손상이 발생할 수 있는지 전혀 모르기 때문에 타사 코드에서는 최악 입니다. 타사 라이브러리가 종료되기에 충분히 강력하지 않은 경우 다음 중 하나를 수행해야합니다. (1) 저자에게 문제를 해결하도록 요청하고, (2) 다른 것을 사용하십시오. 실제로 선택의 여지가 없다면 일부 리소스는 단일 프로세스 내에서만 공유되므로 해당 코드를 별도의 프로세스에 배치하는 것이 더 안전해야합니다.
Phil1970

25

파이썬에서는 스레드를 직접 죽일 수 없습니다.

실제로 Thread (!)가 필요하지 않은 경우 스레딩 패키지 대신 멀티 프로세싱 패키지 를 사용하면 됩니다 . 여기서 프로세스를 종료하려면 메소드를 호출하면됩니다.

yourProcess.terminate()  # kill the process!

Python은 프로세스를 종료합니다 (UNIX에서 SIGTERM 신호를 통해, Windows에서는 TerminateProcess() 호출을 ). 대기열이나 파이프를 사용하는 동안 사용에주의하십시오! (대기열 / 파이프의 데이터가 손상 될 수 있음)

참고 그 multiprocessing.Eventmultiprocessing.Semaphore정확히 동일한 방식으로 작동 threading.Event하고,threading.Semaphore 각각은. 실제로 첫 번째 것은 후자의 복제입니다.

스레드를 사용해야 할 경우 직접 죽일 방법이 없습니다. 그러나 할 수있는 것은 "데몬 스레드" 를 사용하는 것 입니다. 실제로 파이썬에서 스레드는 데몬 으로 플래그 될 수 있습니다 .

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

살아있는 비 데몬 스레드가 남아 있지 않으면 주 프로그램이 종료됩니다. 다시 말해, 메인 스레드 (데몬 이외의 스레드)가 작업을 마치면 여전히 일부 데몬 스레드가 작동하더라도 프로그램이 종료됩니다.

메소드가 호출 daemon되기 전에 스레드를 설정 해야합니다 start()!

물론 당신은 할 수 및 사용해야 daemon와도 multiprocessing. 여기서 기본 프로세스가 종료되면 모든 데몬 하위 프로세스를 종료하려고 시도합니다.

마지막으로,주의하시기 바랍니다 sys.exit()os.kill()선택하지 않습니다.


14

스레드를 종료하는 스레드에 추적을 설치하여 스레드를 강제 종료 할 수 있습니다. 하나의 가능한 구현에 대해서는 첨부 된 링크를 참조하십시오.

파이썬에서 스레드를 죽여


나는 이미 그것을 보았다. 이 솔루션은 self.killed 플래그 검사를 기반으로합니다
Sudden Def

1
실제로 작동하는 몇 가지 답변 중 하나
Ponkadoodle

5
이 솔루션의 두 가지 문제 : (a) sys.settrace ()를 사용하여 추적 프로그램을 설치하면 스레드 실행 속도가 느려집니다. 컴퓨팅 바운드 인 경우 최대 10 배 느려집니다. (b) 시스템 호출 중에 스레드에 영향을 미치지 않습니다.
Matthias Urlichs

10

명시 적으로 호출하는 경우 time.sleep()스레드 (예를 들어 폴링 일부 외부 서비스)의 일환으로, 필립의 방법에 따라 개선은에 타임 아웃을 사용하는 것입니다 eventwait() 어디든지 당신을 방법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()ing 일 때) 종료 의견을 처리하는 코드가 훨씬 간단하다는 것입니다 .


3
이 게시물이 왜 다운 보팅 되었습니까? 이 게시물에 어떤 문제가 있습니까? 그것은 내가 필요한 것과 똑같아 보입니다 ....
JDOaktown

9

스레드를 죽이지 않는 것이 좋습니다. 스레드의주기에 "시도"블록을 도입하고 스레드를 중지하려고 할 때 예외를 발생시키는 방법이있을 수 있습니다 (예 : 중단 / 복귀 / ... for / while / ...을 중지시키는 중단 / 복귀 / ...). 내 앱에서 이것을 사용했으며 작동합니다 ...


8

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클래스입니다.


3
self.__stop스레드에 설정되어 있는지 확인하는 영리한 방법 입니다. 여기에있는 대부분의 다른 솔루션과 마찬가지로 추적 기능은 새로운 로컬 범위가 입력 될 때만 호출되므로 실제로는 차단 호출을 중단하지 않습니다. 또한 sys.settrace실제로 디버거, 프로파일 등을 구현하기위한 것이므로 CPython의 구현 세부 사항으로 간주되며 다른 Python 구현에는 존재하지 않을 수도 있습니다.
dano

3
@ dano : Thread2클래스 의 가장 큰 문제 중 하나 는 코드를 약 10 배 느리게 실행한다는 것입니다. 일부 사람들은 여전히 ​​이것을 받아 들일 수 있습니다.
녹 티스 스카이 타워 September

+1하면 코드 실행 속도가 상당히 느려집니다.이 솔루션의 작성자가이 정보를 답변에 포함시키는 것이 좋습니다.
Vishal

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

tThread객체입니다.

파이썬 소스 ( Modules/threadmodule.cPython/thread_pthread.h)를 읽으십시오. Thread.identpthread_t유형 임을 알 수 있으므로 pthread파이썬 사용에서 할 수있는 모든 것을 할 수 있습니다 libpthread.


그리고 Windows에서 어떻게합니까?
iChux

12
당신은하지 않습니다; Windows가 아니라 Linux가 아닙니다. 이유 :이 작업을 수행하는 동안 문제의 스레드가 GIL을 보유 할 수 있습니다 (Python은 C를 호출 할 때 GIL을 해제합니다). 그렇다면 프로그램이 즉시 교착 상태가됩니다. 그렇지 않은 경우에도 마지막으로 블록이 실행되지 않으므로 매우 안전하지 않은 아이디어입니다.
Matthias Urlichs

6

스레드를 죽이려면 다음 해결 방법을 사용할 수 있습니다.

kill_threads = False

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

thread.start_new_thread(doSomething, ())

이것은 메인 스레드에서 다른 모듈로 작성된 코드의 스레드를 종료하는 경우에도 사용할 수 있습니다. 해당 모듈에서 전역 변수를 선언하고 해당 모듈에서 생성 된 스레드를 종료하는 데 사용할 수 있습니다.

나는 보통 프로그램 종료시 모든 스레드를 종료하기 위해 이것을 사용합니다. 이것은 스레드를 종료하는 완벽한 방법은 아니지만 도움이 될 수 있습니다.


엄지 손가락. 이해하기 쉽다.
alyssaeliyah

6

나는이 게임에 늦었지만 비슷한 질문 으로 어려움을 겪고 있으며 다음은 나에게 완벽하게 문제를 해결하고 데몬 서브 스레드가 종료 될 때 기본적인 스레드 상태 확인 및 정리를 수행하는 것으로 보입니다.

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]

이것은 목록에서 더 높아야 할 훌륭한 답변입니다
drootang

왜 올리는 것이 SystemExitafter_timeout스레드 (단순히이 예에서 출구로 이전에 대기) 메인 스레드에 아무것도 할?
Davis Herring

@DavisHerring 나는 당신이 무엇을 얻고 있는지 잘 모르겠습니다. SystemExit가 메인 스레드를 종료합니다. WOULNTNT가 메인 스레드에서 어떤 작업을 수행한다고 생각하십니까? 이 호출이 없으면 프로그램은 계속 자식 스레드를 기다립니다. ctrl + c를 사용하거나 다른 방법을 사용하여 메인 스레드를 죽일 수도 있지만 이것은 예입니다.
빈민가

@slumtrimpet : SystemExit두 가지 특별한 속성 만 있습니다 : 하나의 스레드를 던져서 종료 할 때 트레이스 백을 생성하지 않으며 메인 스레드가 하나를 던져 종료하면 종료 상태를 설정합니다 (그럼에도 불구하고 다른 비 데몬 스레드를 기다리는 중) 나가기 위해서).
Davis Herring

@DavisHerring 아, 알았어. 당신의 의견에 완전히 찬성합니다. 우리가 여기서 한 일을 잘못 기억하고 귀하의 의견을 오해했습니다.
빈민가

4

내가 추가하고 싶은 한 가지는 lib Python 스레딩의 공식 문서를 읽는 경우 Paolo Rovelli가 언급 한 플래그와 함께 스레드가 갑자기 끝나기를 원하지 않을 때 "민속적"스레드를 사용하지 않는 것이 좋습니다 .

공식 문서에서 :

종료시 데몬 스레드가 갑자기 중지됩니다. 해당 파일 (예 : 열린 파일, 데이터베이스 트랜잭션 등)이 제대로 해제되지 않을 수 있습니다. 스레드가 정상적으로 중지되도록하려면 스레드를 비 대조적으로 만들고 이벤트와 같은 적절한 신호 메커니즘을 사용하십시오.

나는 데몬 스레드를 만드는 것이 응용 프로그램에 달려 있다고 생각하지만 일반적으로 (그리고 제 생각에는) 그들을 죽이거나 데몬으로 만드는 것을 피하는 것이 좋습니다. 멀티 프로세싱에서는 다음을 사용할 수 있습니다.is_alive() 프로세스 상태를 확인하고 완료를 "종료"하는 데 사용할 수 있습니다 (GIL 문제도 방지 함). 그러나 때로는 Windows에서 코드를 실행할 때 더 많은 문제를 찾을 수 있습니다.

그리고 "실제 스레드"가 있으면 파이썬 인터프리터가 실행되기를 기다립니다. (이 데몬 때문에 갑자기 문제가 해결되지 않으면 도움이 될 수 있습니다).


4
나는 마지막 단락을 이해하지 못한다.
tshepang

@Tshepang 응용 프로그램에 실행중인 비 단어 스레드가있는 경우 모든 비 데몬 스레드가 완료 될 때까지 Python 인터프리터는 계속 실행 됩니다 . 프로그램이 종료 될 때 스레드가 종료되는지 여부를 신경 쓰지 않으면 데몬을 사용할 수 있습니다.
Tom Myddeltyn

3

이 목적으로 만들어진 라이브러리 인 stopit이 있습니다. 여기에 나열된 동일한주의 사항 중 일부가 여전히 적용되지만, 적어도이 라이브러리는 명시된 목표를 달성하기위한 규칙적이고 반복 가능한 기술을 제공합니다.


1

오히려 오래된이지만, 이것은 일부에 대한 편리한 해결책이 될 수 있습니다

스레딩의 모듈 기능을 확장하는 작은 모듈-한 스레드가 다른 스레드와 관련하여 예외를 발생시킬 수 있습니다. 를 올리면 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)

따라서 "스레드가 다른 스레드의 컨텍스트에서 예외를 발생시킬 수 있습니다"를 허용하므로 종료 된 스레드는 중단 플래그를 정기적으로 확인하지 않고 종료를 처리 할 수 ​​있습니다.

그러나 원래 소스 에 따르면 이 코드에는 몇 가지 문제가 있습니다.

  • 파이썬 바이트 코드를 실행할 때만 예외가 발생합니다. 스레드가 기본 / 내장 차단 함수를 호출하면 실행이 파이썬 코드로 돌아올 때만 예외가 발생합니다.
    • 내장 함수가 내부적으로 PyErr_Clear ()를 호출하면 보류중인 예외를 효과적으로 취소하는 문제도 있습니다. 다시 올릴 수 있습니다.
  • 예외 유형 만 안전하게 발생할 수 있습니다. 예외 인스턴스는 예기치 않은 동작을 유발할 수 있으므로 제한됩니다.
  • 내장 스레드 모듈에서이 함수를 공개하도록 요청했지만 ctypes가 표준 라이브러리 (2.5 기준)가되었으므로이
    기능은 구현에 구애받지 않을 가능성이 있으므로
    노출되지 않은 상태로 유지 될 수 있습니다 .

0

이것은 Windows 7에서 pywin32와 함께 작동하는 것 같습니다

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

0

Pieter Hintjens- ØMQ 설립자 중 한 명 프로젝트 사용하고 잠금, 뮤텍스, 이벤트 등과 같은 동기화 기본 요소를 피하는 것이 멀티 스레드 프로그램을 작성하는 가장 안전하고 안전한 방법이라고 말합니다.

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

여기에는 자식 스레드에게 작업을 취소해야한다는 것을 포함합니다. 스레드에 ØMQ 소켓을 장착하고 해당 소켓에서 폴링하여 취소해야한다는 메시지를 폴링하면됩니다.

이 링크는 또한 ØMQ를 사용하는 다중 스레드 파이썬 코드에 대한 예를 제공합니다.


0

동일한 함수의 여러 스레드를 원한다고 가정하면 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 이면 중지하십시오 .


0

사용자 정의 함수를 사용하여 KillableThread 하위 클래스를 만들려면 @SCB의 아이디어 (정확히 필요한 것)를 구축하십시오.

from threading import Thread, Event

class KillableThread(Thread):
    def __init__(self, sleep_interval=1, target=None, name=None, args=(), kwargs={}):
        super().__init__(None, target, name, args, kwargs)
        self._kill = Event()
        self._interval = sleep_interval
        print(self._target)

    def run(self):
        while True:
            # Call custom function with arguments
            self._target(*self._args)

        # 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()

if __name__ == '__main__':

    def print_msg(msg):
        print(msg)

    t = KillableThread(10, print_msg, args=("hello world"))
    t.start()
    time.sleep(6)
    print("About to kill thread")
    t.kill()

당연히 @SBC와 마찬가지로 스레드는 중지하기 위해 새 루프를 실행하기를 기다리지 않습니다. 이 예에서는 스레드가 완료 될 때까지 4 초 이상 기다리지 않고 "스레드 종료 정보"바로 다음에 "킬링 스레드"메시지가 인쇄되는 것을 볼 수 있습니다 (이미 6 초 동안 잤기 때문에).

KillableThread 생성자의 두 번째 인수는 사용자 정의 함수입니다 (여기서는 print_msg). Args 인수는 여기서 함수 (( "hello world"))를 호출 할 때 사용되는 인수입니다.


0

@ Kozyarchuk의 답변 에서 언급했듯이 추적 설치가 작동합니다. 이 답변에는 코드가 포함되어 있지 않으므로 다음은 바로 사용할 수있는 예제입니다.

import sys, threading, time 

class TraceThread(threading.Thread): 
    def __init__(self, *args, **keywords): 
        threading.Thread.__init__(self, *args, **keywords) 
        self.killed = False
    def start(self): 
        self._run = self.run 
        self.run = self.settrace_and_run
        threading.Thread.start(self) 
    def settrace_and_run(self): 
        sys.settrace(self.globaltrace) 
        self._run()
    def globaltrace(self, frame, event, arg): 
        return self.localtrace if event == 'call' else None
    def localtrace(self, frame, event, arg): 
        if self.killed and event == 'line': 
            raise SystemExit() 
        return self.localtrace 

def f(): 
    while True: 
        print('1') 
        time.sleep(2)
        print('2') 
        time.sleep(2)
        print('3') 
        time.sleep(2)

t = TraceThread(target=f) 
t.start() 
time.sleep(2.5) 
t.killed = True

또한 인쇄 한 후 정지 1하고 2. 3인쇄되지 않습니다.


-1

프로세스에서 명령을 실행 한 다음 프로세스 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;

-1

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

-2

이것은 나쁜 답변입니다, 의견을 참조하십시오

방법은 다음과 같습니다.

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().


threading.Thread 객체 내부에서 self._Thread__stop ()을 사용하여 중지했습니다. 이 예제와 같은 간단한 self.join () ( code.activestate.com/recipes/65448-thread-control-idiom )이 작동하지 않는 이유를 이해할 수 없습니다.
harijay

12
"실제로 스레드를 멈추지 않습니다"에 대한 자세한 내용은 도움이 될 것입니다.
2371

19
기본적으로 _Thread__stop 메소드를 호출해도 스레드가 중지되었음을 Python에 알리는 것 외에 다른 효과는 없습니다. 실제로 계속 실행할 수 있습니다. 예는 gist.github.com/2787191 을 참조하십시오 .
Bluehorn

35
이것은 명백한 잘못입니다. _Thread__stop()단순히 마크는 중지로 스레드 , 실제로는 스레드를 중지하지 않습니다! 절대 이러지 마 읽어보십시오 .
dotancohen

-2

하위 작업을 종료하는 기능이 실제로 필요한 경우 대체 구현을 사용하십시오. multiprocessing그리고 gevent둘 다 무차별 적으로 "스레드"를 죽이는 것을 지원합니다.

파이썬의 스레딩은 취소를 지원하지 않습니다. 시도하지도 마라. 코드가 메모리 교착 상태, 손상 또는 누수 또는 드물고 비 결정적으로 발생하는 의도하지 않은 "흥미로운"디버그하기 어려운 효과가 발생할 가능성이 큽니다.


2
… 그리고 그렇습니다. 나는 둘 다 엄격하게 "스레딩"이 아니라는 것을 알고 있습니다. 그러나 코드가 모델에 맞거나 맞을 수 있으면 둘 다 작동합니다.
Matthias Urlichs
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.