Apakah ada cara untuk membunuh Thread?

767

Apakah mungkin untuk menghentikan thread yang sedang berjalan tanpa mengatur / memeriksa flag / semaphores / etc?

Def Mendadak
sumber

Jawaban:

672

Biasanya merupakan pola yang buruk untuk membunuh utas secara tiba-tiba, dengan Python dan dalam bahasa apa pun. Pikirkan kasus-kasus berikut:

  • utas memegang sumber daya kritis yang harus ditutup dengan benar
  • utas telah menciptakan beberapa utas lain yang harus dibunuh juga.

Cara yang baik untuk menangani ini jika Anda mampu membelinya (jika Anda mengelola utas Anda sendiri) adalah memiliki flag exit_request yang diperiksa masing-masing utas pada interval reguler untuk melihat apakah sudah saatnya ia keluar.

Sebagai contoh:

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

Dalam kode ini, Anda harus memanggil stop()utas saat Anda ingin keluar, dan menunggu utas keluar dengan benar join(). Utas harus memeriksa tanda berhenti secara berkala.

Namun ada beberapa kasus ketika Anda benar-benar perlu membunuh utas. Contohnya adalah ketika Anda membungkus perpustakaan eksternal yang sibuk untuk panggilan lama dan Anda ingin menghentikannya.

Kode berikut memungkinkan (dengan beberapa batasan) untuk meningkatkan Pengecualian dalam utas 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 )

(Didasarkan pada Killable Threads oleh Tomer Filiba. Kutipan tentang nilai pengembalian PyThreadState_SetAsyncExctampaknya berasal dari versi lama Python .)

Seperti dicatat dalam dokumentasi, ini bukan peluru ajaib karena jika utas sibuk di luar juru bahasa Python, itu tidak akan menangkap gangguan.

Pola penggunaan yang baik dari kode ini adalah untuk membuat thread menangkap pengecualian khusus dan melakukan pembersihan. Dengan begitu, Anda dapat mengganggu tugas dan masih harus melakukan pembersihan yang benar.

Philippe F
sumber
78
@ Bluebird75: Selanjutnya, saya tidak yakin saya mendapatkan argumen bahwa utas tidak boleh dibunuh secara tiba-tiba "karena utas mungkin memegang sumber daya kritis yang harus ditutup dengan benar": ini juga berlaku dari program utama, dan program utama dapat dibunuh secara tiba-tiba oleh pengguna (Ctrl-C di Unix, misalnya) –dalam hal ini mereka mencoba menangani kemungkinan ini sebaik mungkin. Jadi, saya gagal melihat apa yang spesial dengan utas, dan mengapa mereka tidak seharusnya menerima perlakuan yang sama dengan program utama (yaitu bahwa mereka dapat dibunuh secara tiba-tiba). :) Bisakah Anda menjelaskan hal ini?
Eric O Lebigot
18
@ EOL: Di sisi lain, jika semua sumber daya yang dimiliki thread adalah sumber daya lokal (file terbuka, soket), Linux cukup baik dalam proses pembersihan dan ini tidak bocor. Saya punya kasus di mana saya membuat server menggunakan soket, dan jika saya melakukan gangguan brutal dengan Ctrl-C, saya tidak bisa lagi meluncurkan program karena tidak dapat mengikat soket. Saya perlu menunggu 5 menit. Solusi yang tepat adalah dengan menangkap Ctrl-C dan melakukan deconnection soket yang bersih.
Philippe F
10
@ Bluebird75: btw. Anda dapat menggunakan SO_REUSEADDRopsi soket untuk menghindari Address already in usekesalahan.
Messa
12
Catatan tentang jawaban ini: setidaknya untuk saya (py2.6), saya harus lulus Nonealih-alih 0untuk res != 1kasus ini, dan saya harus memanggil ctypes.c_long(tid)dan meneruskannya ke fungsi ctypes daripada tid secara langsung.
Walt W
21
Nilainya menyebutkan bahwa _stop sudah ditempati di pustaka threading Python 3. Dengan demikian, mungkin menggunakan variabel yang berbeda jika tidak, Anda akan mendapatkan kesalahan.
diesthreetimes
113

Tidak ada API resmi untuk melakukan itu, tidak.

Anda perlu menggunakan platform API untuk mematikan utas, mis. Pthread_kill, atau TerminateThread. Anda dapat mengakses API seperti misalnya melalui pythonwin, atau melalui ctypes.

Perhatikan bahwa ini pada dasarnya tidak aman. Kemungkinan akan mengarah pada sampah yang tidak dapat ditagih (dari variabel lokal dari kerangka tumpukan yang menjadi sampah), dan dapat menyebabkan kebuntuan, jika utas yang terbunuh memiliki GIL pada titik ketika terbunuh.

Martin v. Löwis
sumber
26
Ini akan menyebabkan kebuntuan jika utas yang bersangkutan menampung GIL.
Matthias Urlichs
96

Sebuah multiprocessing.Processkalengp.terminate()

Dalam kasus di mana saya ingin membunuh utas, tetapi tidak ingin menggunakan flag / kunci / sinyal / semaphores / peristiwa / apa pun, saya mempromosikan utas ke proses penuh. Untuk kode yang hanya menggunakan beberapa utas, biaya overhead tidak terlalu buruk.

Misalnya ini berguna untuk dengan mudah mengakhiri helper "utas" yang mengeksekusi pemblokiran I / O

Konversi sepele: Dalam kode terkait ganti semua threading.Threaddengan multiprocessing.Processdan semua queue.Queuedengan multiprocessing.Queuedan tambahkan panggilan yang diperlukan p.terminate()untuk proses induk Anda yang ingin membunuh anaknyap

Lihat dokumentasi Python untukmultiprocessing .

cfi
sumber
Terima kasih. Saya mengganti queue.Queue dengan multiprocessing.JoinableQueue dan mengikuti jawaban ini: stackoverflow.com/a/11984760/911207
David Braun
Banyak halaman tentang masalah ini. Ini sepertinya solusi yang jelas bagi banyak saya
geotheory
6
multiprocessingitu bagus, tetapi perlu diperhatikan bahwa argumen acar ke proses baru. Jadi jika salah satu argumen adalah sesuatu yang tidak dapat dipilih (seperti a logging.log) itu mungkin bukan ide yang baik untuk digunakan multiprocessing.
Lyager
1
multiprocessingargumen acar ke proses baru di Windows, tetapi Linux menggunakan forking untuk menyalinnya (Python 3.7, tidak yakin apa versi lain). Jadi, Anda akan berakhir dengan kode yang berfungsi di Linux tetapi menimbulkan kesalahan acar pada Windows.
nyanpasu64
multiprocessingdengan logging adalah bisnis yang sulit. Perlu digunakan QueueHandler(lihat tutorial ini ). Saya mempelajarinya dengan cara yang sulit.
Fanchen Bao
74

Jika Anda mencoba untuk menghentikan seluruh program, Anda dapat mengatur utas sebagai "daemon". Lihat Thread.daemon

schettino72
sumber
Ini tidak masuk akal. Dokumentasi dengan jelas menyatakan, "ini harus diatur sebelum start () dipanggil, jika tidak RuntimeError dimunculkan." Jadi, jika saya ingin membunuh utas yang awalnya bukan daemon, bagaimana saya bisa menggunakan ini?
Raffi Khatchadourian
27
Raffi Saya pikir dia menyarankan Anda untuk menyetelnya terlebih dahulu, mengetahui bahwa ketika utas utama Anda keluar, Anda juga ingin agar daemon utas keluar.
fantabolous
1
Tidak yakin mengapa ini bukan jawaban yang diterima
eric
Bukankah menetapkan utas sebagai daemon adalah sesuatu yang akan Anda lakukan seandainya Anda ingin utas tetap berjalan meskipun program utama Anda dimatikan?
Michele Piccolini
Anda tuan, adalah pahlawan saya hari ini. Persis apa yang saya cari dan tidak perlu repot untuk menambahkan.
Blizz
42

Seperti yang telah disebutkan orang lain, normanya adalah untuk menetapkan tanda berhenti. Untuk sesuatu yang ringan (tidak ada subkelas Thread, tidak ada variabel global), panggilan balik lambda adalah pilihan. (Perhatikan tanda kurung di 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()

Mengganti print()dengan pr()fungsi yang selalu flush ( sys.stdout.flush()) dapat meningkatkan presisi keluaran shell.

(Hanya diuji pada Windows / Eclipse / Python3.3)

Jon Coombs
sumber
1
Diverifikasi pada Linux / Python 2.7, bekerja seperti pesona. Ini harus menjadi jawaban resmi, itu jauh lebih sederhana.
Paul Kenjora
1
Diverifikasi pada Linux Ubuntu Server 17.10 / Python 3.6.3 dan berfungsi.
Marcos
1
Juga diverifikasi di 2.7. Jawaban yang sangat bagus!
silgon
Apa itu pr()fungsi?
alper
35

Ini didasarkan pada thread2 - utas dibunuh (resep Python)

Anda perlu memanggil PyThreadState_SetasyncExc (), yang hanya tersedia melalui ctypes.

Ini hanya diuji pada Python 2.7.3, tetapi kemungkinan akan bekerja dengan rilis 2.x terbaru lainnya.

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")
Johan Dahlin
sumber
Saya menggunakan sesuatu seperti ini untuk memberikan utas saya KeyboardInterruptsehingga mereka memiliki kesempatan untuk membersihkan. Jika mereka MASIH menggantung setelah itu, maka SystemExitsudah tepat, atau hanya membunuh proses dari terminal.
drevicko
Ini berfungsi jika utas sedang dijalankan. Ini tidak berfungsi jika utasnya dalam syscall; pengecualian akan diabaikan secara diam-diam.
Matthias Urlichs
@MatthiasUrlichs ada ide bagaimana mendeteksi apa status eksekusi utas, untuk dapat mencetak peringatan atau coba lagi?
Johan Dahlin
1
@JohanDahlin Anda bisa menunggu sedikit (yang, jika Anda ingin mencoba lagi, Anda harus tetap melakukannya) dan kemudian lakukan tes isAlive (). Bagaimanapun, sementara ini akan berhasil, saya juga tidak akan menjamin bahwa itu tidak meninggalkan referensi menggantung. Meskipun secara teori dimungkinkan untuk membuat thread thread aman di CPython, dengan penggunaan yang bijaksana pthread_cleanup_push()/_pop(), itu akan banyak pekerjaan untuk diterapkan dengan benar dan itu akan memperlambat penerjemah secara nyata.
Matthias Urlichs
32

Anda seharusnya tidak pernah secara paksa membunuh utas tanpa bekerja sama dengannya.

Membunuh utas menghapus jaminan apa pun yang mencoba / akhirnya memblokir penyetelan sehingga Anda dapat membiarkan kunci terkunci, file terbuka, dll.

Satu-satunya waktu Anda dapat berdebat bahwa membunuh thread secara paksa adalah ide yang bagus adalah dengan mematikan sebuah program dengan cepat, tetapi tidak pernah satu thread pun.

Lasse V. Karlsen
sumber
11
Mengapa begitu sulit untuk hanya memberi tahu, silakan bunuh diri ketika Anda menyelesaikan perulangan Anda saat ini ... Saya tidak mengerti.
Mehdi
2
Tidak ada mekanisme yang dibangun ke dalam cpu untuk mengidentifikasi "loop" seperti itu, yang terbaik yang dapat Anda harapkan adalah dengan menggunakan semacam sinyal bahwa kode yang saat ini berada di dalam loop akan memeriksa setelah keluar. Cara yang benar untuk menangani sinkronisasi utas adalah dengan cara kooperatif, penskorsan, melanjutkan, dan membunuh utas adalah fungsi yang dimaksudkan untuk debugger dan sistem operasi, bukan kode aplikasi.
Lasse V. Karlsen
2
@Mehdi: jika saya (secara pribadi) menulis kode di utas, ya, saya setuju dengan Anda. Tetapi ada beberapa kasus di mana saya menjalankan perpustakaan pihak ketiga, dan saya tidak memiliki akses ke loop eksekusi kode itu. Itu adalah satu kasus penggunaan untuk fitur yang diminta.
Dan H
@DanH Bahkan lebih buruk lagi dengan kode pihak ketiga karena Anda tidak tahu apa penyebabnya. Jika perpustakaan pihak ketiga Anda tidak cukup kuat sehingga harus dibunuh, maka Anda harus melakukan salah satu dari ini: (1) minta penulis untuk memperbaiki masalah, (2) menggunakan sesuatu yang lain. Jika Anda benar-benar tidak punya pilihan, maka menempatkan kode itu dalam proses yang berbeda akan lebih aman karena beberapa sumber daya hanya dibagikan dalam satu proses tunggal.
Phil1970
25

Dengan Python, Anda tidak bisa langsung membunuh Thread.

Jika Anda TIDAK benar-benar perlu memiliki Thread (!), Yang dapat Anda lakukan, daripada menggunakan paket threading , adalah dengan menggunakan paket multiprosesing . Di sini, untuk mematikan suatu proses, Anda cukup memanggil metode:

yourProcess.terminate()  # kill the process!

Python akan mematikan proses Anda (di Unix melalui sinyal SIGTERM, sementara di Windows melalui TerminateProcess()panggilan). Perhatikan untuk menggunakannya saat menggunakan Antrian atau Pipa! (mungkin merusak data dalam Antrian / Pipa)

Perhatikan bahwa multiprocessing.Eventdan multiprocessing.Semaphorekerja sama persis dengan cara threading.Eventdan threading.Semaphoremasing - masing. Faktanya, yang pertama adalah klon dari latters.

Jika Anda BENAR-BENAR perlu menggunakan Thread, tidak ada cara untuk membunuhnya secara langsung. Apa yang dapat Anda lakukan adalah menggunakan "daemon thread" . Bahkan, dalam Python, sebuah Thread dapat ditandai sebagai daemon :

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

Program utama akan keluar ketika tidak ada utas non-daemon yang tersisa. Dengan kata lain, ketika utas utama Anda (yang, tentu saja, utas non-daemon) akan menyelesaikan operasinya, program akan keluar bahkan jika masih ada beberapa utas daemon yang berfungsi.

Perhatikan bahwa perlu untuk mengatur Thread daemonsebelum start()metode dipanggil!

Tentu saja Anda dapat, dan harus, menggunakannya daemonbahkan dengan multiprocessing. Di sini, ketika proses utama keluar, ia mencoba untuk menghentikan semua proses anak daemonic.

Akhirnya, tolong, perhatikan itu sys.exit()dan os.kill()bukan pilihan.

Paolo Rovelli
sumber
14

Anda dapat membunuh utas dengan menginstal jejak ke utas yang akan keluar utas. Lihat tautan terlampir untuk satu kemungkinan implementasi.

Bunuh utas dengan Python

Kozyarchuk
sumber
Saya sudah melihatnya. Solusi ini didasarkan pada pemeriksaan bendera yang dilakukan sendiri
Sudden Def
1
Salah satu dari sedikit jawaban di sini yang benar-benar BEKERJA
Ponkadoodle
5
Dua masalah dengan solusi ini: (a) memasang pelacak dengan sys.settrace () akan membuat utas Anda berjalan lebih lambat. Sebanyak 10 kali lebih lambat jika dihitung terikat. (B) tidak akan mempengaruhi utas Anda saat sedang dalam panggilan sistem.
Matthias Urlichs
10

Jika Anda secara eksplisit menelepon time.sleep()sebagai bagian dari utas Anda (katakanlah polling beberapa layanan eksternal), peningkatan pada metode Phillipe adalah dengan menggunakan batas waktu di event'swait() metode mana pun Andasleep()

Sebagai contoh:

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

Lalu untuk menjalankannya

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

Keuntungan menggunakan wait()daripada sleep()memeriksa dan secara teratur memeriksa acara adalah bahwa Anda dapat memprogram dalam interval tidur yang lebih lama, utas dihentikan segera (ketika Anda seharusnya sleep()) dan menurut pendapat saya, kode untuk menangani keluar secara signifikan lebih sederhana. .

SCB
sumber
3
mengapa postingan ini diturunkan? Apa yang salah dengan posting ini? Itu terlihat persis seperti apa yang saya butuhkan ....
JDOaktown
9

Lebih baik jika Anda tidak membunuh utas. Sebuah cara bisa dengan memperkenalkan blok "coba" ke dalam siklus utas dan untuk melemparkan pengecualian ketika Anda ingin menghentikan utas (misalnya, break / return / ... yang menghentikan Anda untuk / while / ...). Saya telah menggunakan ini di aplikasi saya dan berfungsi ...

Giancarlo
sumber
8

Sangat mungkin untuk menerapkan Thread.stopmetode seperti yang ditunjukkan pada kode contoh berikut:

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

The Thread3kelas muncul untuk menjalankan kode sekitar 33% lebih cepat dari Thread2kelas.

Noctis Skytower
sumber
3
Ini adalah cara cerdas untuk menyuntikkan cek untuk self.__stopyang ditetapkan ke utas. Perhatikan bahwa seperti kebanyakan solusi lain di sini, itu tidak akan benar-benar mengganggu panggilan pemblokiran, karena fungsi penelusuran hanya dipanggil saat ruang lingkup lokal baru dimasukkan. Juga patut dicatat adalah yang sys.settracebenar - benar dimaksudkan untuk mengimplementasikan debugger, profil, dll. Dan dengan demikian dianggap sebagai detail implementasi CPython, dan tidak dijamin ada dalam implementasi Python lainnya.
dano
3
@ano: Salah satu masalah terbesar dengan Thread2kelas adalah bahwa ia menjalankan kode kira-kira sepuluh kali lebih lambat. Beberapa orang mungkin masih menganggap ini dapat diterima.
Noctis Skytower
+1 pada ini sangat memperlambat eksekusi kode .. Saya menyarankan agar penulis solusi ini memasukkan informasi ini dalam jawabannya.
Vishal
6
from ctypes import *
pthread = cdll.LoadLibrary("libpthread-2.15.so")
pthread.pthread_cancel(c_ulong(t.ident))

t adalah Threadobjekmu.

Baca sumber python ( Modules/threadmodule.cdan Python/thread_pthread.h) yang bisa Anda lihat Thread.identadalah pthread_ttipe, jadi Anda bisa melakukan apa saja yang pthreadbisa dilakukan dalam penggunaan python libpthread.

snyh
sumber
Dan bagaimana Anda melakukan ini pada Windows?
iChux
12
Kamu tidak; tidak pada Windows dan juga pada Linux. Alasan: Utas yang dipermasalahkan mungkin menahan GIL saat Anda melakukan ini (Python melepaskan GIL saat Anda menelepon ke C). Jika ya, program Anda akan langsung menemui jalan buntu. Bahkan jika tidak, akhirnya: blok tidak akan dieksekusi dll, jadi ini adalah ide yang sangat tidak aman.
Matthias Urlichs
6

Solusi berikut dapat digunakan untuk membunuh utas:

kill_threads = False

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

thread.start_new_thread(doSomething, ())

Ini dapat digunakan bahkan untuk mengakhiri utas, yang kodenya ditulis dalam modul lain, dari utas utama. Kita dapat mendeklarasikan variabel global dalam modul itu dan menggunakannya untuk mengakhiri utas yang dihasilkan dalam modul itu.

Saya biasanya menggunakan ini untuk mengakhiri semua utas pada saat keluar dari program. Ini mungkin bukan cara yang sempurna untuk mengakhiri utas tetapi dapat membantu.

Amit Chahar
sumber
Jempolan. Mudah dimengerti.
alyssaeliyah
6

Saya sangat terlambat ke permainan ini, tetapi saya telah bergulat dengan pertanyaan yang sama dan yang berikut ini muncul untuk menyelesaikan masalah dengan sempurna bagi saya DAN memungkinkan saya melakukan beberapa pemeriksaan dan pembersihan status utas ketika sub-utas yang diemoni keluar:

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

Hasil:

0
1
KILL MAIN THREAD: 140013208254208
'CLEANLY' kill sub-thread with value: 2 [THREAD: 140013674317568]
slumtrimpet
sumber
Ini adalah jawaban yang sangat baik yang seharusnya lebih tinggi dalam daftar
drootang
Mengapa mengangkat SystemExitpada after_timeoututas melakukan apa saja pada utas utama (yang hanya menunggu yang sebelumnya keluar dalam contoh ini)?
Davis Herring
@ Davidviser Saya tidak yakin apa yang Anda maksud. SystemExit membunuh utas utama, mengapa menurut Anda TIDAK AKAN melakukan apa pun pada utas utama? Tanpa panggilan itu, program hanya akan terus menunggu di utas anak. Anda juga bisa ctrl + c atau menggunakan cara lain untuk mematikan utas utama tetapi ini adalah contohnya.
slumtrimpet
@slumtrimpet: SystemExithanya memiliki dua properti khusus: ia tidak menghasilkan traceback (ketika ada utas keluar dengan melemparkan satu), dan jika utas utama keluar dengan melemparkan satu, ia menetapkan status keluar (sementara tetap menunggu utas non-daemon lainnya) untuk keluar).
Davis Herring
@ Davidviser Oh, aku mengerti. Sepenuhnya setuju dengan Anda. Saya salah mengingat apa yang kami lakukan di sini dan salah mengerti komentar Anda.
slumtrimpet
4

Satu hal yang ingin saya tambahkan adalah bahwa jika Anda membaca dokumentasi resmi dalam threading lib Python , disarankan untuk menghindari penggunaan utas "setan", ketika Anda tidak ingin utas berakhir tiba-tiba, dengan bendera yang disebutkan Paolo Rovelli .

Dari dokumentasi resmi:

Utas daemon tiba-tiba berhenti saat dimatikan. Sumber daya mereka (seperti file terbuka, transaksi basis data, dll.) Mungkin tidak dirilis dengan benar. Jika Anda ingin utas Anda berhenti dengan anggun, buatlah itu tidak daemonik dan gunakan mekanisme pensinyalan yang sesuai seperti Peristiwa.

Saya pikir membuat daemonic thread tergantung pada aplikasi Anda, tetapi secara umum (dan menurut saya) lebih baik untuk menghindari membunuhnya atau menjadikannya daemonic. Di multiprocessing bisa Anda gunakanis_alive() pemrosesan untuk memeriksa status proses dan "mengakhiri" untuk menyelesaikannya (Anda juga menghindari masalah GIL). Tetapi Anda dapat menemukan lebih banyak masalah, kadang-kadang, ketika Anda mengeksekusi kode Anda di Windows.

Dan selalu ingat bahwa jika Anda memiliki "utas langsung", interpreter Python akan berjalan menunggu mereka. (Karena ini daemonic dapat membantu Anda jika tidak masalah berakhir tiba-tiba).

Chema
sumber
4
Saya tidak mengerti paragraf terakhir.
tshepang
@Tshepang Ini berarti bahwa jika ada utas non-daemonic yang berjalan dalam aplikasi Anda, juru bahasa Python akan terus berjalan sampai semua utas non-daemon selesai . Jika Anda tidak peduli apakah utas berakhir ketika program berakhir, maka menjadikannya daemon bisa berguna.
Tom Myddeltyn
3

Ada perpustakaan yang dibangun untuk tujuan ini, stopit . Meskipun beberapa peringatan yang sama yang tercantum di sini masih berlaku, setidaknya perpustakaan ini menyajikan teknik yang teratur dan berulang untuk mencapai tujuan yang dinyatakan.

Jason R. Coombs
sumber
1

Meskipun agak lama, ini mungkin solusi yang berguna untuk beberapa orang:

Modul kecil yang memperluas fungsionalitas modul threading - memungkinkan satu utas untuk meningkatkan pengecualian dalam konteks utas lainnya. Dengan menaikkan SystemExit, Anda akhirnya dapat membunuh utas python.

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)

Jadi, ini memungkinkan "utas untuk meningkatkan pengecualian dalam konteks utas lain" dan dengan cara ini, utas yang dihentikan dapat menangani penghentian tanpa secara teratur memeriksa bendera batalkan.

Namun, menurut sumber aslinya , ada beberapa masalah dengan kode ini.

  • Pengecualian akan dimunculkan hanya ketika menjalankan bytecode python. Jika utas Anda memanggil fungsi pemblokiran bawaan / bawaan, pengecualian akan dimunculkan hanya ketika eksekusi kembali ke kode python.
    • Ada juga masalah jika fungsi internal secara internal memanggil PyErr_Clear (), yang secara efektif akan membatalkan pengecualian yang tertunda. Anda dapat mencoba menaikkannya lagi.
  • Hanya tipe pengecualian yang dapat dinaikkan dengan aman. Contoh pengecualian cenderung menyebabkan perilaku yang tidak terduga, dan karenanya dibatasi.
  • Saya diminta untuk memaparkan fungsi ini dalam modul ulir bawaan, tetapi karena ctypes telah menjadi pustaka standar (pada 2.5), dan
    fitur ini tidak mungkin sebagai implementasi-agnostik, ia mungkin tetap
    tidak terpapar.
wp78de
sumber
0

Ini sepertinya bekerja dengan pywin32 di windows 7

my_thread = threading.Thread()
my_thread.start()
my_thread._Thread__stop()
zzart
sumber
0

Pieter Hintjens - salah satu pendiri proyek ØMQ - mengatakan, menggunakan ØMQ dan menghindari primitif sinkronisasi seperti kunci, mutex, acara, dll., Adalah cara paling aman dan aman untuk menulis program multi-utas:

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

Ini termasuk memberi tahu thread anak, bahwa itu harus membatalkan pekerjaannya. Ini akan dilakukan dengan melengkapi utas dengan ØMQ-socket dan polling pada soket itu untuk pesan yang mengatakan bahwa itu harus dibatalkan.

Tautan ini juga memberikan contoh pada kode python multi-threaded dengan ØMQ.

paulkernstock
sumber
0

Asuming, bahwa Anda ingin memiliki beberapa utas dari fungsi yang sama, ini adalah IMHO implementasi termudah untuk menghentikan satu oleh 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

Yang menyenangkan ada di sini, Anda dapat memiliki banyak fungsi yang sama dan berbeda, dan menghentikan mereka semua functionname.stop

Jika Anda hanya ingin memiliki satu utas fungsi maka Anda tidak perlu mengingat id. Berhenti saja, jika doit.stop> 0.

rundekugel
sumber
0

Hanya untuk membangun ide @ SCB (yang persis seperti yang saya butuhkan) untuk membuat subkelas KillableThread dengan fungsi khusus:

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

Secara alami, seperti dengan @SBC, utas tidak menunggu untuk menjalankan loop baru untuk berhenti. Dalam contoh ini, Anda akan melihat pesan "Killing Thread" dicetak tepat setelah "About to kill thread" alih-alih menunggu 4 detik untuk menyelesaikan utas (karena kami sudah tidur selama 6 detik).

Argumen kedua dalam konstruktor KillableThread adalah fungsi khusus Anda (print_msg di sini). Argumen args adalah argumen yang akan digunakan saat memanggil fungsi (("hello world")) di sini.

Tim Meehan
sumber
0

Seperti disebutkan dalam jawaban @ Kozyarchuk , menginstal jejak berfungsi. Karena jawaban ini tidak mengandung kode, berikut adalah contoh siap pakai yang berfungsi:

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

Itu berhenti setelah dicetak 1dan 2. 3tidak dicetak.

Basj
sumber
-1

Anda dapat menjalankan perintah Anda dalam suatu proses dan kemudian membunuhnya menggunakan id proses. Saya perlu menyinkronkan antara dua utas yang salah satunya tidak kembali dengan sendirinya.

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;
pengguna1942887
sumber
-1

Mulai sub utas dengan 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
Sud
sumber
-2

Ini jawaban yang buruk, lihat komentar

Inilah cara melakukannya:

from threading import *

...

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

Berikan beberapa detik, maka utas Anda harus dihentikan. Periksa jugathread._Thread__delete() metodenya.

Saya akan merekomendasikan thread.quit()metode untuk kenyamanan. Misalnya, jika Anda memiliki soket di utas Anda, saya sarankan membuat quit()metode di kelas pegangan-soket Anda, mengakhiri soket, kemudian jalankan bagian thread._Thread__stop()dalam Anda quit().

DoXiD
sumber
Saya harus menggunakan self._Thread__stop () di dalam objek threading.Thread saya untuk menghentikannya. Saya tidak mengerti mengapa self.join () yang sama seperti contoh ini ( code.activestate.com/recipes/65448-thread-control-idiom ) tidak berfungsi.
harijay
12
Rincian lebih lanjut tentang "ini tidak benar-benar menghentikan utas" akan sangat membantu.
2371
19
Pada dasarnya, memanggil metode _Thread__stop tidak berpengaruh selain memberi tahu Python bahwa utas dihentikan. Ini benar-benar dapat terus berjalan. Lihat gist.github.com/2787191 untuk contoh.
Bluehorn
35
Ini jelas salah. _Thread__stop()hanya menandai utas sebagai terhenti , itu tidak benar-benar menghentikan utas! Tidak pernah melakukan ini. Baca .
dotancohen
-2

Jika Anda benar-benar membutuhkan kemampuan untuk membunuh sub-tugas, gunakan implementasi alternatif. multiprocessingdan geventkeduanya mendukung pembunuhan tanpa ampun "utas".

Threading Python tidak mendukung pembatalan. Jangan coba-coba. Kode Anda sangat mungkin menemui jalan buntu, rusak atau bocor memori, atau memiliki efek sulit lain yang tidak diinginkan "menarik" yang jarang terjadi dan tidak ditentukan nasibnya.

Matthias Urlichs
sumber
2
... dan ya, saya tahu bahwa keduanya tidak sepenuhnya "threading", tetapi mereka berdua berfungsi jika kode Anda cocok (atau dapat dibuat agar sesuai) model mereka.
Matthias Urlichs