문제

플래그/세마포어/등을 설정/확인하지 않고 실행 중인 스레드를 종료할 수 있습니까?

도움이 되었습니까?

해결책

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).반환 값에 대한 인용문 PyThreadState_SetAsyncExc 출신인 것 같다 이전 버전의 Python.)

문서에 언급된 대로 스레드가 Python 인터프리터 외부에서 사용 중이면 중단을 포착하지 못하기 때문에 이것은 마법의 총알이 아닙니다.

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

다른 팁

공식 API가 없습니다.

플랫폼 API를 사용하여 스레드 (예 : pthread_kill 또는 TerminateThread)를 죽여야합니다. Pythonwin 또는 CTypes를 통해 이러한 API 예를 들어 액세스 할 수 있습니다.

이것은 본질적으로 안전하지 않습니다. 수집 할 수없는 쓰레기 (쓰레기가되는 스택 프레임의 로컬 변수에서)로 이어질 수 있으며, 사망 한 실이 죽을 때의 길에 길이있는 경우 교착 상태로 이어질 수 있습니다.

multiprocessing.Process ~할 수 있다 p.terminate()

스레드를 죽이고 싶지만 플래그/잠금/신호/세마포어/이벤트/uthing을 사용하고 싶지 않은 경우 스레드를 완전히 날려 버린 프로세스로 홍보합니다. 몇 스레드 만 사용하는 코드의 경우 오버 헤드가 그렇게 나쁘지 않습니다.

예 : 이것은 차단 I/O를 실행하는 헬퍼 "스레드"를 쉽게 종료하기 위해 편리합니다.

변환은 사소한 일입니다. 관련 코드에서 모두 교체 threading.Thread ~와 함께 multiprocessing.Process 그리고 다 queue.Queue ~와 함께 multiprocessing.Queue 필요한 전화를 추가하십시오 p.terminate() 자녀를 죽이고 싶은 부모 과정에 p

파이썬 문서

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

이것은 기반입니다 Thread2- 킬 가능한 스레드 (파이썬 레시피)

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

당신은 실을 협력하지 않고 강제로 죽이지 않아야합니다.

스레드를 죽이면 시도/마침내 블록이 설정되어 잠금 장치를 잠그거나 파일을 열게 할 수 있다는 보증이 제거됩니다.

스레드를 강제로 죽이는 것이 좋은 아이디어라고 주장 할 수있는 유일한 시간은 프로그램을 빨리 죽이는 것이지만 단일 스레드는 아닙니다.

다른 사람들이 언급했듯이, 표준은 정지 깃발을 설정하는 것입니다. 가벼운 것 (스레드의 서브 클래스, 글로벌 변수 없음)의 경우 Lambda 콜백이 옵션입니다. (괄호 안에 주목하십시오 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() a pr() 항상 플러시하는 기능 (sys.stdout.flush()) 쉘 출력의 정밀도를 향상시킬 수 있습니다.

(Windows/Eclipse/Python3.3에서만 테스트)

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

실제로 스레드 (!)가 필요하지 않으면 사용하는 대신 할 수있는 일 스레딩 패키지 ,다중 프로세싱 패키지 . 여기서 프로세스를 죽이려면 단순히 방법을 호출 할 수 있습니다.

yourProcess.terminate()  # kill the process!

Python은 프로세스를 죽입니다 (Sigterm 신호를 통해 Unix에서 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() 선택이 아닙니다.

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

파이썬에서 실을 죽이십시오

실을 죽이지 않으면 더 좋습니다. 방법은 스레드의 사이클에 "시도"블록을 도입하고 스레드를 멈추고 싶을 때 예외를 던지는 것입니다 (예 : break/return/...로서/while/...). 나는 이것을 내 앱에서 사용했고 작동합니다 ...

a를 구현할 수 있습니다 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 수업.

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

너의 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, ())

코드가 다른 모듈로 작성된 스레드를 종료하는데도 사용할 수 있습니다. 해당 모듈의 글로벌 변수를 선언하고 해당 모듈에서 스폰 된 스레드를 종료하는 데 사용할 수 있습니다.

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

내가 추가하고 싶은 한 가지는 공식 문서를 읽는 것입니다. 스레딩 리브 파이썬, Paolo Rovelli의 깃발과 함께 실이 갑자기 끝나는 것을 원하지 않을 때 "Demonic"실의 사용을 피하는 것이 좋습니다. 말하는.

공식 문서에서 :

셧다운시 데몬 스레드가 갑자기 중지됩니다. 그들의 리소스 (예 : 열린 파일, 데이터베이스 트랜잭션 등)는 제대로 해제되지 않을 수 있습니다. 스레드가 우아하게 멈추기를 원한다면, 비 데모닉으로 만들고 이벤트와 같은 적절한 신호 메커니즘을 사용하십시오.

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

그리고 "라이브 스레드"가 있다면 Python 통역사가 대기하기 위해 실행됩니다. (이 데몬이 갑자기 끝나지 않으면 도움이 될 수 있습니다).

명시 적으로 전화하는 경우 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()이벤트를 정기적으로 확인하는 것은 더 긴 수면 간격으로 프로그래밍 할 수 있다는 것입니다. 스레드는 거의 즉시 중지됩니다 (그렇지 않으면 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]

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

이것은 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

좀 오래된 것 같긴 하지만, 이것 일부에게는 편리한 솔루션이 될 수 있습니다.

스레딩의 모듈 기능을 확장하는 작은 모듈 - 한 스레드가 다른 스레드의 컨텍스트에서 예외를 발생시킬 수 있습니다. 스레드.키워서 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()를 내부적으로 호출하는 경우에도 문제가 있습니다. 이는 보류 중인 예외를 효과적으로 취소합니다.다시 올려보시면 됩니다.
  • 예외 유형만 안전하게 발생시킬 수 있습니다.예외 인스턴스는 예기치 않은 동작을 일으킬 가능성이 높으므로 제한됩니다.
  • 내장 스레드 모듈에 이 함수를 노출하도록 요청했지만 ctypes가 표준 라이브러리(2.5 기준)가 되었기 때문에 이것은
    기능은 구현에 구애받지 않을 가능성이 높으며 유지될 수 있습니다.
    노출되지 않은.

Pieter Hintjens- 창립자 중 하나 ØMQ-project- ØMQ를 사용하고 잠금, 뮤트, 이벤트 등과 같은 동기화 프리미티브를 피하는 것은 다중 스레드 프로그램을 작성하는 가장 엄청나게 안전한 방법입니다.

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

여기에는 자식 스레드에게 말하고 작업을 취소해야한다고 말합니다. 이는 스레드를 ØMQ 소켓으로 장착하고 소켓에 폴링을 장착하여 취소해야한다는 메시지로 수행됩니다.

링크는 또한 ØMQ와 함께 다중 스레드 파이썬 코드의 예제를 제공합니다.

프로세스에서 명령을 실행 한 다음 프로세스 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의 스레딩은 취소를 지원하지 않습니다. 시도하지도 마라. 코드는 교착 상태, 부패 또는 누출 메모리가 발생할 가능성이 높거나 거의 의도하지 않은 "흥미로운"다른 "흥미로운"어려운 영향을 미치지 않으며, 거의 무체한 영향을 미치지 않습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top