Bir Konuyu öldürmenin bir yolu var mı?


Yanıtlar:


672

Bir ipliği Python'da ve herhangi bir dilde aniden öldürmek genellikle kötü bir modeldir. Aşağıdaki durumları düşünün:

  • iş parçacığı düzgün kapatılması gereken kritik bir kaynak tutuyor
  • evre, öldürülmesi gereken başka evreler de yarattı.

Ödeyebiliyorsanız bunu yapmanın güzel yolu (kendi iş parçacıklarınızı yönetiyorsanız), her iş parçacığının çıkma zamanının gelip gelmediğini görmek için düzenli aralıklarla kontrol ettiği bir exit_request bayrağına sahip olmaktır.

Örneğin:

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

Bu kodda, stop()çıkmasını istediğinizde iş parçacığını çağırmanız ve iş parçacığının kullanarak doğru şekilde çıkmasını beklemeniz gerekir join(). İplik durdurma bayrağını düzenli aralıklarla kontrol etmelidir.

Bununla birlikte, bir iş parçacığını gerçekten öldürmeniz gerektiğinde durumlar vardır. Bir örnek, uzun aramalar için meşgul olan harici bir kütüphaneyi sararken ve onu kesmek istediğinizde verilebilir.

Aşağıdaki kod (bazı kısıtlamalarla) Python iş parçacığında bir İstisna oluşturmaya izin verir:

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 tarafından yazılan Killable Threads temel alınmıştır. Dönüş değeri hakkındaki alıntı , Python'un eski bir versiyonundanPyThreadState_SetAsyncExc geliyor gibi görünüyor .)

Belgelerde belirtildiği gibi, bu sihirli bir mermi değildir, çünkü iplik Python yorumlayıcısının dışında meşgulse, kesintiyi yakalamaz.

Bu kodun iyi bir kullanım şekli, iş parçacığının belirli bir özel durumu yakalaması ve temizleme işlemini gerçekleştirmesidir. Bu şekilde, bir görevi kesintiye uğratabilir ve yine de düzgün bir şekilde temizleyebilirsiniz.


78
@ Bluebird75: Ayrıca, iş parçacıklarının aniden öldürülmemesi gerektiği argümanı elde ettiğimden emin değilim. kullanıcı tarafından aniden öldürülebilir (örneğin, Unix'te Ctrl-C) - bu durumda bu olasılığı olabildiğince iyi ele almaya çalışırlar. Bu yüzden, iş parçacıkları ile neyin özel olduğunu ve neden ana programlarla aynı tedaviyi almamalarını göremiyorum (yani aniden öldürülebilirler). :) Bu konuda ayrıntılı misiniz?
Eric O Lebigot

18
@EOL: Öte yandan, iş parçacığının sahip olduğu tüm kaynaklar yerel kaynaklarsa (açık dosyalar, yuvalar), Linux işlem temizliğinde makul derecede iyidir ve bu sızıntı yapmaz. Soket kullanarak bir sunucu oluşturduğum durumlarda da vardı ve Ctrl-C ile acımasız bir kesinti yaparsam, artık soketi bağlayamadığı için programı başlatamıyorum. 5 dakika beklemem gerek. Uygun çözüm Ctrl-C'yi yakalamak ve soket bağlantısının temizliğini yapmaktı.
Philippe F

10
@ Bluebird75: btw. hatayı SO_REUSEADDRönlemek için soket seçeneğini kullanabilirsiniz Address already in use.
Messa

12
Bu yanıtla ilgili Not: Benim (py2.6) için en azından, ben geçmek zorunda Noneyerine 0ilişkin res != 1söz ve ben aramak zorunda ctypes.c_long(tid)ve herhangi ctypes doğrudan tid yerine işlev olduğunu geçmektedir.
Walt W

21
_Stop'un zaten Python 3 iş parçacığı kütüphanesinde işgal edildiğinden bahsetmeye değer. Bu nedenle, belki farklı bir değişken kullanın aksi takdirde bir hata alırsınız.
ölenler bazen

113

Bunu yapacak resmi bir API yok, hayır.

İş parçacığını öldürmek için platform API'sini kullanmanız gerekir, örn. Pthread_kill veya TerminateThread. Bu tür API'lara örneğin pythonwin veya ctypes aracılığıyla erişebilirsiniz.

Bunun doğal olarak güvensiz olduğuna dikkat edin. Muhtemelen toplanamayan çöplere (çöp haline gelen yığın çerçevelerinin yerel değişkenlerinden) yol açacaktır ve eğer öldürülmekte olan iplik öldürüldüğü noktada GIL varsa, kilitlenmelere yol açabilir.


26
Bu olacak söz konusu iplik GIL tutarsa çözümsüzlüklerle yol açar.
Matthias Urlichs

96

Bir multiprocessing.Processkutup.terminate()

Bir iş parçacığını öldürmek istediğim, ancak bayrakları / kilitleri / sinyalleri / semaforları / olayları / diğerlerini kullanmak istemediğim durumlarda, iş parçacıklarını tam üflemeli işlemlere teşvik ediyorum. Sadece birkaç iş parçacığından yararlanan kod için ek yük o kadar da kötü değildir.

Örneğin, bu, G / Ç'yi engelleyen yardımcı "iş parçacıklarını" kolayca sonlandırmak için kullanışlıdır

Dönüşüm Önemsiz: İlgili kodunda tümünü değiştir threading.Threadile multiprocessing.Processve tüm queue.Queueile multiprocessing.Queueve gerekli aramaları eklemek p.terminate()onun çocuğu öldürmek istiyor Ebeveyninizin sürecinep

İçin Python belgelerine bakınmultiprocessing .


Teşekkürler. Ben multiprocessing.JoinableQueue ile queue.Queue yerini ve bu cevabı izledi: stackoverflow.com/a/11984760/911207
David Braun

Bu konuda birçok sayfa. Bu bence birçok kişi için bariz bir çözüm gibi görünüyor
geotheory

6
multiprocessinggüzel, ancak argümanların yeni sürece yönlendirildiğini unutmayın. Dolayısıyla, argümanlardan biri seçilemez bir şeyse (a gibi logging.log) kullanmak iyi bir fikir olmayabilir multiprocessing.
Lyager

1
multiprocessingargümanlar Windows'taki yeni işleme getirilir, ancak Linux bunları kopyalamak için çatal kullanır (Python 3.7, diğer sürümlerden emin değilsiniz). Böylece Linux üzerinde çalışan ancak Windows'ta turşu hatalarını yükselten kod elde edersiniz.
nyanpasu64

multiprocessinggiriş ile zor bir iştir. Kullanmanız gerekiyor QueueHandler( bu eğiticiye bakın ). Zor yoldan öğrendim.
Fanchen Bao

74

Tüm programı sonlandırmaya çalışıyorsanız, iş parçacığını "arka plan programı" olarak ayarlayabilirsiniz. bkz. Thread.daemon


Bu hiç mantıklı değil. Belgeler açıkça "start () çağrılmadan önce ayarlanmalıdır, aksi takdirde RuntimeError kaldırılır." Böylece, aslında bir daemon olmayan bir iş parçacığını öldürmek istersem bunu nasıl kullanabilirim?
Raffi Khatchadourian

27
Raffi Sanırım, ana iş parçanızdan çıktığında da daemon iş parçacıklarının çıkmasını istediğinizi bilerek, önceden ayarlayacağınızı önerir.
fantastik

1
Bunun neden kabul edilen cevap olmadığından emin değilim
eric

Ana programınız kapatılsa bile iş parçacığının çalışmaya devam etmesini istemeniz durumunda bir iş parçacığını arka plan programı olarak ayarlamıyor musunuz?
Michele Piccolini

Siz efendim, günün kahramanım. Tam olarak ne aradığını ve eklemek için hiçbir yaygara.
Blizz

42

Diğerlerinin de belirttiği gibi, norm bir durdurma bayrağı koymaktır. Hafif bir şey için (Thread'in alt sınıfı yok, global değişken yok), lambda geri çağırma bir seçenektir. (İçindeki parantezleri not edin 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()

Her zaman yıkanan ( ) print()bir pr()işlevle değiştirmek sys.stdout.flush(), kabuk çıktısının hassasiyetini artırabilir.

(Yalnızca Windows / Eclipse / Python3.3 üzerinde test edilmiştir)


1
Linux / Python 2.7'de doğrulandı, bir cazibe gibi çalışıyor. Bu resmi cevap olmalı, çok daha basit.
Paul Kenjora

1
Linux Ubuntu Server 17.10 / Python 3.6.3 üzerinde doğrulandı ve çalışıyor.
Marcos

1
Ayrıca 2.7. Ne güzel bir cevap!
silgon

Nedir pr()işlevi?
alper

35

Bu thread2 - fırınlanabilir iplikleri temel alır (Python tarifi)

Yalnızca ctypes aracılığıyla kullanılabilen PyThreadState_SetasyncExc () öğesini çağırmanız gerekir.

Bu sadece Python 2.7.3 üzerinde test edilmiştir, ancak diğer son 2.x sürümleriyle de çalışması muhtemeldir.

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

İpliklerimi KeyboardInterrupttemizlemek için böyle bir şey kullanıyorum, böylece temizlik şansı var. Bundan sonra HALA asılı kalırlarsa SystemExit, uygundur veya işlemi bir terminalden öldürün.
drevicko

Bu, iş parçacığı şu anda yürütülüyorsa çalışır. İş parçacığı bir sistem çağrısı içindeyse çalışmaz; istisna sessizce göz ardı edilecektir.
Matthias Urlichs

@MatthiasUrlichs, iş parçacığı yürütme durumunun ne olduğunu nasıl algılayacağına, bir uyarı yazdırabildiğine veya yeniden deneyebileceğine dair herhangi bir fikre sahip mi?
Johan Dahlin

1
@JohanDahlin Biraz bekleyebilirsin (denemek isterseniz yine de yapmanız gerekir) ve sonra isAlive () testini yapın. Her halükarda, bu işe yarayacak olsa da, etrafta sarkan referanslar bırakmadığını garanti etmem. Teoride, CPython'da iplik öldürmeyi güvenli hale getirmek mümkün olsa da, makul bir şekilde kullanılmasıyla, pthread_cleanup_push()/_pop()doğru bir şekilde uygulanması çok iş olurdu ve yorumlayıcıyı belirgin şekilde yavaşlatacaktır.
Matthias Urlichs

32

Bir ipliği onunla işbirliği yapmadan asla zorla öldürmemelisiniz.

Bir iş parçacığını öldürmek, kilitleri kilitli, dosyaları açık vb. Bırakabilmeniz için kurulumu / sonlandırmayı engellemeyi garanti eden tüm garantileri kaldırır.

Zorla iş parçacığı öldürmenin iyi bir fikir olduğunu iddia edebileceğiniz tek zaman, bir programı hızlı bir şekilde öldürmek, ancak asla tek bir iş parçacığını öldürmektir.


11
Neden sadece bir konu anlatmak bu kadar zor ki, lütfen mevcut döngünüzü bitirdiğinizde kendinizi öldürün ... Anlamadım.
Mehdi

2
İşlemcide bir "döngü" tanımlamak için yerleşik bir mekanizma yoktur, umduğunuz en iyi şey, döngü içinde bulunan kodun çıktıktan sonra kontrol edeceği bir tür sinyal kullanmaktır. İş parçacığı senkronizasyonunu işlemenin doğru yolu işbirlikçi yöntemlerle yapılır, iş parçacıklarının askıya alınması, devam ettirilmesi ve öldürülmesi uygulama kodu değil, hata ayıklayıcılar ve işletim sistemi için kullanılan işlevlerdir.
Lasse V. Karlsen

2
@Mehdi: Eğer kodu (şahsen) iş parçacığına yazarsam, evet, sana katılıyorum. Ancak üçüncü taraf kitaplıkları çalıştırdığım durumlar var ve bu kodun yürütme döngüsüne erişimim yok. Bu, istenen özellik için bir kullanım durumudur.
Dan H

@DanH Ne kadar zarar verebileceğine dair hiçbir fikriniz olmadığı için üçüncü taraf koduyla bile en kötüsü . Üçüncü taraf kitaplığınız öldürülmesi gereken kadar sağlam değilse, aşağıdakilerden birini yapmanız gerekir: (1) yazardan sorunu düzeltmesini isteyin, (2) başka bir şey kullanın. Gerçekten başka seçeneğiniz yoksa, bazı kaynaklar yalnızca tek bir işlemde paylaşıldığından bu kodu farklı bir sürece koymak daha güvenli olmalıdır.
Phil1970

25

Python'da, bir Konuyu doğrudan öldüremezsiniz.

Gerçekten bir iş parçacığına (!) İhtiyacınız DEĞİLSE, iş parçacığı paketini kullanmak yerine yapabileceğiniz çok işlemcili paketi kullanmaktır . Burada, bir işlemi öldürmek için yöntemi çağırabilirsiniz:

yourProcess.terminate()  # kill the process!

Python işleminizi öldürecektir (Unix'te SIGTERM sinyaliyle, Windows'da TerminateProcess()çağrı yoluyla ). Bir Kuyruk veya Pipo kullanırken kullanmaya dikkat edin! (Kuyruk / Borudaki verileri bozabilir)

Not olduğunu multiprocessing.Eventve multiprocessing.Semaphoreaynı şekilde tam olarak çalışma threading.Eventve threading.Semaphoresırasıyla. Aslında, ilki latterin klonlarıdır.

GERÇEKTEN bir Konu kullanmanız gerekiyorsa, onu doğrudan öldürmenin bir yolu yoktur. Ancak yapabileceğiniz şey, bir "daemon thread" kullanmaktır . Aslında, Python'da, bir Konu arka plan programı olarak işaretlenebilir :

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

Hiçbir canlı daemon olmayan iş parçacığı kalmadığında ana programdan çıkılır. Başka bir deyişle, ana iş parçanız (tabii ki bir daemon olmayan iş parçacığı) işlemlerini tamamladığında, hala çalışan bazı daemon iş parçacıkları olsa bile programdan çıkılır.

Yöntem çağrılmadan daemonönce bir Konu ayarlamak gerektiğini unutmayın start()!

Tabii ki daemonbile kullanabilirsiniz ve kullanmalısınız multiprocessing. Burada, ana süreç çıktığında, tüm daemonik alt süreçlerini sonlandırmaya çalışır.

Son olarak, lütfen, unutmayın sys.exit()ve os.kill()seçenek değildir.


14

İş parçacığından çıkacak iş parçacığına izleme yükleyerek bir iş parçacığını öldürebilirsiniz. Olası bir uygulama için ekteki bağlantıya bakın.

Python'da bir iş parçacığını öldür


Ben zaten gördüm. Bu çözüm, kendi kendini öldüren bayrak kontrolüne dayanıyor
Sudden Def

1
Burada gerçekten
işe yarayan

5
Bu çözümle ilgili iki sorun: (a) sys.settrace () ile bir izleyici kurmak iş parçacığınızın daha yavaş çalışmasını sağlayacaktır. Hesaplamaya bağlıysa 10 kat daha yavaştır. (b) sistem çağrısındayken iş parçacığınızı etkilemez.
Matthias Urlichs

10

Eğer time.sleep()iş parçacığınızın bir parçası olarak açıkça çağırıyorsanız (diyelim ki harici hizmet çağırma), Phillipe'nin yöntemindeki bir zaman aşımı, nerede olursanız olun event' wait()yönteminde zaman aşımı kullanmaktır.sleep()

Örneğin:

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

Sonra çalıştırmak için

t = KillableThread(sleep_interval=5)
t.start()
# Every 5 seconds it prints:
#: Do Something
t.kill()
#: Killing Thread

Olayı ing olarak kullanmak ve düzenli olarak kontrol etmek wait()yerine kullanmanın avantajı, sleep()daha uzun uyku aralıklarında programlayabilmeniz, iplik neredeyse anında durur (aksi halde olursanız sleep()) ve bence, çıkışla ilgili kod önemli ölçüde daha basittir .


3
bu gönderi neden reddedildi? Bu yazıda yanlış olan ne? Tam ihtiyacım gibi görünüyor ....
JDOaktown

9

Bir ipliği öldürmezseniz daha iyi olur. Bir yol, iş parçacığının döngüsüne bir "try" bloğu eklemek ve iş parçacığını durdurmak istediğinizde bir istisna atmak olabilir (örneğin, for / while / ... komutunu durduran bir break / return / ...). Bunu uygulamamda kullandım ve işe yarıyor ...


8

Thread.stopAşağıdaki örnek kodda gösterildiği gibi bir yöntem uygulamak kesinlikle mümkündür :

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

Thread3Sınıf yaklaşık% 33 daha hızlı kod çalıştırmasına görünen Thread2sınıfın.


3
Bu, self.__stopiş parçacığına yerleştirilmek üzere kontroller enjekte etmenin akıllıca bir yoludur . İzleme işlevinin yalnızca yeni bir yerel kapsam girildiğinde çağrıldığından, buradaki diğer çözümlerin çoğu gibi, aslında bir engelleme çağrısını kesintiye uğratmayacağını unutmayın. Ayrıca, sys.settracehata ayıklayıcıları, profilleri vb. Uygulamak için gerçekten amaçlandığı ve bu şekilde CPython'un bir uygulama detayı olarak kabul edildiği ve diğer Python uygulamalarında varlığının garanti edilmediği de belirtilmelidir.
dano

3
@dano: Sınıfla ilgili en büyük sorunlardan biri, Thread2kodu yaklaşık on kat daha yavaş çalıştırmasıdır. Bazı insanlar bunu hala kabul edilebilir bulabilir.
Noctis Skytower

Bu +1 kodu kod yürütmeyi önemli ölçüde yavaşlatır .. Bu çözümün yazarının bu bilgiyi yanıta eklemesini öneririm.
Vishal

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

t senin olan Threadnesne.

Python kaynağını okuyun ( Modules/threadmodule.cve Python/thread_pthread.h) Thread.identbir pthread_ttür olduğunu görebilirsiniz, böylece pthreadpython kullanımında yapabileceğiniz her şeyi yapabilirsiniz libpthread.


Ve bunu Windows'ta nasıl yapıyorsunuz?
iChux

12
Sen yapmazsın; Windows'da değil Linux'ta da değil. Sebep: Söz konusu iş parçacığı bunu yaparken GIL tutabilir (C'yi çağırdığınızda Python GIL'i serbest bırakır). Varsa, programınız anında kilitlenecektir. Olmasa bile, nihayet: bloklar yürütülmeyecek vb, bu yüzden bu çok güvensiz bir fikirdir.
Matthias Urlichs

6

Bir iş parçacığını öldürmek için aşağıdaki geçici çözüm kullanılabilir:

kill_threads = False

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

thread.start_new_thread(doSomething, ())

Bu, kodu başka bir modülde yazılmış olan evreleri ana evreden sonlandırmak için bile kullanılabilir. Bu modülde global bir değişken bildirebilir ve bu modülde ortaya çıkan evreleri sonlandırmak için kullanabiliriz.

Bunu genellikle program çıkışındaki tüm evreleri sonlandırmak için kullanırım. Bu, iş parçacıklarını sonlandırmak için mükemmel bir yol olmayabilir, ancak yardımcı olabilir.


Başparmak havaya. Anlaması basit.
alyssaeliyah

6

Bu oyuna çok geç kaldım, ama benzer bir soru ile güreştim ve aşağıdakiler hem benim için mükemmel bir şekilde sorunu çözmek için görünüyor VE ben de arka plana alınan alt-iş parçacığı çıktığında bazı temel iş parçacığı durumu denetimi ve temizliği yapmama izin veriyor:

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

Verim:

0
1
KILL MAIN THREAD: 140013208254208
'CLEANLY' kill sub-thread with value: 2 [THREAD: 140013674317568]

Bu, listede daha yüksek olması gereken mükemmel bir cevaptır
drootang

Neden yükselterek olurdu SystemExitüzerindeki after_timeoutiplik (sadece bu örnekte çıkmak için eski bekliyor) ana iş parçacığı için her şeyi?
Davis Herring

@DavisHerring Ne elde ettiğinden emin değilim. SystemExit ana iş parçacığını öldürür, neden ana iş parçacığında bir şey yapmasını düşünüyorsunuz? Bu çağrı olmadan, program sadece alt dizide beklemeye devam edecektir. Ayrıca ana iş parçacığını öldürmek için ctrl + c veya başka bir yol kullanabilirsiniz, ancak bu bir örnektir.
slumtrimpet

@slumtrimpet: SystemExityalnızca iki özel özelliğe sahiptir: bir geri izleme oluşturmaz (herhangi bir iş parçacığı fırlatarak çıktığında) ve ana iş parçacığı fırlatılarak çıkarsa çıkış durumunu ayarlar (yine de diğer daemon olmayan iş parçacıklarını beklerken çıkışa doğru).
Davis Herring

@DavisHerring Oh, yakaladım. Sana tamamen katılıyorum. Burada yaptığımızı yanlış anlıyordum ve yorumunuzu yanlış anladım.
slumtrimpet

4

Eklemek istediğim bir şey , lib Python iş parçacığında resmi belgeleri okursanız , Paolo Rovelli'nin bahsettiği bayrakla, iş parçacıklarının aniden sona ermesini istemediğinizde "şeytani" iş parçacıklarının kullanılmasından kaçınılmasıdır .

Resmi belgelerden:

Daemon iş parçacıkları kapatma sırasında aniden durdurulur. Kaynakları (açık dosyalar, veritabanı işlemleri vb.) Doğru şekilde serbest bırakılmayabilir. İş parçacıklarınızın zarif bir şekilde durmasını istiyorsanız, onları daemon olmayan yapın ve bir Etkinlik gibi uygun bir sinyal mekanizması kullanın.

Daemonic iş parçacığı oluşturmanın uygulamanıza bağlı olduğunu düşünüyorum, ancak genel olarak (ve bence) onları öldürmekten veya onları daemon yapmaktan daha iyi. Çoklu işlemlerde şunları kullanabilirsiniz:is_alive() işlemde, işlem durumunu kontrol etmek ve bitirmek için "sonlandırmak" için kullanabilirsiniz (Ayrıca GIL sorunlarından kaçınabilirsiniz). Ancak bazen kodunuzu Windows'da yürüttüğünüzde daha fazla sorun bulabilirsiniz.

Ve "canlı iş parçacıkları" varsa, Python yorumlayıcısının onları beklemeye çalışacağını unutmayın. (Bu daemonic nedeniyle aniden bitmezse size yardımcı olabilir).


4
Son paragrafı anlamıyorum.
tshepang

Uygulamanızda herhangi çalıştıran olmayan daemonic evre varsa dek Python yorumlayıcısı çalışmaya devam edeceğini @Tshepang Bu araçlar tüm olmayan cini ipler edilir yapılır . Program sona erdiğinde iş parçacıklarının bitip bitmeyeceği umrunda değilse, onları arka plan programı yapmak faydalı olabilir.
Tom Myddeltyn

3

Bu amaçla yapılmış bir kütüphane var, stopit . Her ne kadar burada listelenen aynı uyarılar hala geçerli olsa da, en azından bu kütüphane belirtilen hedefe ulaşmak için düzenli ve tekrarlanabilir bir teknik sunmaktadır.


1

Oldukça eski olsa da, bu bazıları için kullanışlı bir çözüm olabilir:

İş parçacığının modül işlevini genişleten küçük bir modül - bir iş parçacığının başka bir iş parçacığı bağlamında istisnalar oluşturmasına izin verir. Yükselterek SystemExit, nihayet piton ipliklerini öldürebilirsin.

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)

Bu nedenle, "iş parçacığı başka bir iş parçacığı bağlamında istisnaları artırmaya" izin verir ve bu yolla sonlandırılmış iş parçacığı, durdurma bayrağını düzenli olarak denetlemeden sonlandırmayı işleyebilir.

Ancak, orijinal kaynağına göre , bu kodla ilgili bazı sorunlar var.

  • Kural dışı durum yalnızca python bayt kodu yürütülürken ortaya çıkar. İleti diziniz yerel / yerleşik engelleme işlevini çağırırsa, istisna yalnızca yürütme python koduna döndüğünde ortaya çıkar.
    • Yerleşik işlev dahili olarak PyErr_Clear () öğesini çağırırsa, bu durum bekleyen istisnanızı etkili bir şekilde iptal ederse de bir sorun vardır. Tekrar yükseltmeyi deneyebilirsiniz.
  • Yalnızca kural dışı durum türleri güvenle oluşturulabilir. İstisna örneklerinin beklenmedik davranışlara neden olması muhtemeldir ve bu nedenle sınırlıdır.
  • Yerleşik iş parçacığı modülünde bu işlevi ortaya çıkarmak istedim, ancak ctypes (2.5'ten itibaren) standart bir kitaplık haline geldiğinden ve bu
    özelliğin uygulamadan bağımsız olması muhtemel olmadığından,
    maruz kalmayabilir .

0

Bu windows 7 üzerinde pywin32 ile çalışıyor gibi görünüyor

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

0

Pieter Hintjens - kurucularından ØMQ kullanarak ve kilitler, muteksler, olaylar vb. Gibi senkronizasyon ilkellerinden kaçınmanın çok iş parçacıklı programlar yazmanın en güvenli ve güvenli yoludur:

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

Bu, çocuk iş parçacığının çalışmasını iptal etmesi gerektiğini bildirmeyi içerir. Bu, iş parçacığının bir ØMQ soketi ile donatılması ve iptal edilmesi gerektiğini söyleyen bir mesaj için o sokette yoklama yapılmasıyla yapılır.

Bağlantı ayrıca ØMQ ile çok iş parçacıklı python koduna bir örnek sağlar.


0

Aynı işlevin birden çok iş parçacığına sahip olmak istediğinizi varsayarsak, bu IMHO'yu id ile durdurmak için en kolay uygulamadır:

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

Güzel olan şey burada, aynı ve farklı işlevlerden birden fazlasına sahip olabilir ve hepsini durdurabilirsiniz. functionname.stop

Eğer işlevin sadece bir iş parçacığına sahip olmak istiyorsanız o zaman kimliği hatırlamanıza gerek yoktur. Sadece doit.stop> 0 ise dur .


0

Özelleştirilmiş bir işleve sahip bir KillableThread alt sınıfı oluşturmak için @ SCB'nin fikrini (tam da ihtiyacım olan şeydi) oluşturmak için:

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

Doğal olarak, @SBC'de olduğu gibi, iş parçacığı durdurmak için yeni bir döngü çalıştırmak için beklemez. Bu örnekte, iş parçacığının tamamlanması için 4 saniye daha beklemek yerine "İş parçacığını öldürmek üzere" ifadesinin hemen ardından "İş parçacığı öldürme" mesajının yazdırıldığını görürsünüz (zaten 6 saniye uyuduk).

KillableThread yapıcısındaki ikinci argüman sizin özel işlevinizdir (print_msg burada). Args argümanı, işlevi (("merhaba dünya")) çağırırken kullanılacak argümanlardır.


0

Kozyarchuk en @ belirtildiği gibi cevap , yüklemeden iz çalışır. Bu yanıt kod içermediğinden, çalışmaya hazır bir örnek:

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

Yazdırdıktan sonra durur 1ve 2. 3yazdırılmaz.


-1

Komutunuzu bir işlemde yürütebilir ve ardından işlem kimliğini kullanarak öldürebilirsiniz. Biri kendi başına dönmeyen iki iş parçacığı arasında eşitlemek gerekiyordu.

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

Alt iş parçacığını setDaemon (True) ile başlatın.

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

Bu kötü bir cevap, yorumlara bakın

Bunu nasıl yapacağınız aşağıda açıklanmıştır:

from threading import *

...

for thread in enumerate():
    if thread.isAlive():
        try:
            thread._Thread__stop()
        except:
            print(str(thread.getName()) + ' could not be terminated'))

Birkaç saniye verin, sonra ipliğin durdurulması gerekir. thread._Thread__delete()Yöntemi de kontrol edin .

thread.quit()Kolaylık sağlamak için bir yöntem öneriyorum . Örneğin, iş parçacığınızda bir soket varsa quit(), soket tanıtıcı sınıfınızda bir yöntem oluşturmanızı , soketi sonlandırmanızı ve ardından bir thread._Thread__stop()iç kısmını çalıştırmanızı öneririm quit().


İş parçacığımın içinde self._Thread__stop () kullanmak zorunda kaldım. Neden bu örnek ( code.activestate.com/recipes/65448-thread-control-idiom ) gibi basit bir self.join ( ) çalışmadığını anlamıyorum.
harijay

12
"Bu gerçekten bir iş parçacığı durmuyor" hakkında daha fazla bilgi yardımcı olacaktır.
2371

19
Temel olarak, _Thread__stop yöntemini çağırmanın Python'a iş parçacığının durdurulduğunu söylemekten başka bir etkisi yoktur. Aslında çalışmaya devam edebilir. Örnek için gist.github.com/2787191 adresine bakın .
Bluehorn

35
Bu çok yanlış. _Thread__stop()sadece bir iş parçacığı durmuş olarak işaretler , aslında iş parçacığını durdurmaz! Bunu asla yapma. Bir okuyun .
dotancohen

-2

Bir alt görevi gerçekten öldürme yeteneğine ihtiyacınız varsa, alternatif bir uygulama kullanın. multiprocessingvegevent her ikisi de bir "iş parçacığını" gelişigüzel öldürmeyi destekler.

Python'un iş parçacığı iptali iptal etmeyi desteklemez. Deneme bile. Kodunuzun büyük olasılıkla kilitlenmesi, bozulması veya belleğe sızması veya nadiren ve belirsiz bir şekilde meydana gelen istenmeyen "ilginç" hata ayıklaması zor etkileri olabilir.


2
… Ve evet, her ikisi de kesinlikle "iş parçacığı" olmadığını biliyorum, ama her ikisi de kodunuzu modellerine uygun (veya sığdırmak için yapılabilir) çalışır.
Matthias Urlichs
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.