Apakah mungkin untuk menghentikan thread yang sedang berjalan tanpa mengatur / memeriksa flag / semaphores / etc?
sumber
Apakah mungkin untuk menghentikan thread yang sedang berjalan tanpa mengatur / memeriksa flag / semaphores / etc?
Biasanya merupakan pola yang buruk untuk membunuh utas secara tiba-tiba, dengan Python dan dalam bahasa apa pun. Pikirkan kasus-kasus berikut:
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_SetAsyncExc
tampaknya 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.
SO_REUSEADDR
opsi soket untuk menghindariAddress already in use
kesalahan.None
alih-alih0
untukres != 1
kasus ini, dan saya harus memanggilctypes.c_long(tid)
dan meneruskannya ke fungsi ctypes daripada tid secara langsung.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.
sumber
Sebuah
multiprocessing.Process
kalengp.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.Thread
denganmultiprocessing.Process
dan semuaqueue.Queue
denganmultiprocessing.Queue
dan tambahkan panggilan yang diperlukanp.terminate()
untuk proses induk Anda yang ingin membunuh anaknyap
Lihat dokumentasi Python untuk
multiprocessing
.sumber
multiprocessing
itu bagus, tetapi perlu diperhatikan bahwa argumen acar ke proses baru. Jadi jika salah satu argumen adalah sesuatu yang tidak dapat dipilih (seperti alogging.log
) itu mungkin bukan ide yang baik untuk digunakanmultiprocessing
.multiprocessing
argumen 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.multiprocessing
dengan logging adalah bisnis yang sulit. Perlu digunakanQueueHandler
(lihat tutorial ini ). Saya mempelajarinya dengan cara yang sulit.Jika Anda mencoba untuk menghentikan seluruh program, Anda dapat mengatur utas sebagai "daemon". Lihat Thread.daemon
sumber
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()
.)Mengganti
print()
denganpr()
fungsi yang selalu flush (sys.stdout.flush()
) dapat meningkatkan presisi keluaran shell.(Hanya diuji pada Windows / Eclipse / Python3.3)
sumber
pr()
fungsi?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.
sumber
KeyboardInterrupt
sehingga mereka memiliki kesempatan untuk membersihkan. Jika mereka MASIH menggantung setelah itu, makaSystemExit
sudah tepat, atau hanya membunuh proses dari terminal.pthread_cleanup_push()/_pop()
, itu akan banyak pekerjaan untuk diterapkan dengan benar dan itu akan memperlambat penerjemah secara nyata.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.
sumber
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:
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.Event
danmultiprocessing.Semaphore
kerja sama persis dengan carathreading.Event
danthreading.Semaphore
masing - 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 :
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
daemon
sebelumstart()
metode dipanggil!Tentu saja Anda dapat, dan harus, menggunakannya
daemon
bahkan denganmultiprocessing
. Di sini, ketika proses utama keluar, ia mencoba untuk menghentikan semua proses anak daemonic.Akhirnya, tolong, perhatikan itu
sys.exit()
danos.kill()
bukan pilihan.sumber
Anda dapat membunuh utas dengan menginstal jejak ke utas yang akan keluar utas. Lihat tautan terlampir untuk satu kemungkinan implementasi.
Bunuh utas dengan Python
sumber
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 dievent
'swait()
metode mana pun Andasleep()
Sebagai contoh:
Lalu untuk menjalankannya
Keuntungan menggunakan
wait()
daripadasleep()
memeriksa dan secara teratur memeriksa acara adalah bahwa Anda dapat memprogram dalam interval tidur yang lebih lama, utas dihentikan segera (ketika Anda seharusnyasleep()
) dan menurut pendapat saya, kode untuk menangani keluar secara signifikan lebih sederhana. .sumber
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 ...
sumber
Sangat mungkin untuk menerapkan
Thread.stop
metode seperti yang ditunjukkan pada kode contoh berikut:The
Thread3
kelas muncul untuk menjalankan kode sekitar 33% lebih cepat dariThread2
kelas.sumber
self.__stop
yang 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 yangsys.settrace
benar - benar dimaksudkan untuk mengimplementasikan debugger, profil, dll. Dan dengan demikian dianggap sebagai detail implementasi CPython, dan tidak dijamin ada dalam implementasi Python lainnya.Thread2
kelas adalah bahwa ia menjalankan kode kira-kira sepuluh kali lebih lambat. Beberapa orang mungkin masih menganggap ini dapat diterima.t adalah
Thread
objekmu.Baca sumber python (
Modules/threadmodule.c
danPython/thread_pthread.h
) yang bisa Anda lihatThread.ident
adalahpthread_t
tipe, jadi Anda bisa melakukan apa saja yangpthread
bisa dilakukan dalam penggunaan pythonlibpthread
.sumber
Solusi berikut dapat digunakan untuk membunuh utas:
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.
sumber
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:
Hasil:
sumber
SystemExit
padaafter_timeout
utas melakukan apa saja pada utas utama (yang hanya menunggu yang sebelumnya keluar dalam contoh ini)?SystemExit
hanya 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).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:
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 gunakan
is_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).
sumber
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.
sumber
Meskipun agak lama, ini mungkin solusi yang berguna untuk beberapa orang:
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.
sumber
Ini sepertinya bekerja dengan pywin32 di windows 7
sumber
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.
sumber
Asuming, bahwa Anda ingin memiliki beberapa utas dari fungsi yang sama, ini adalah IMHO implementasi termudah untuk menghentikan satu oleh id:
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.sumber
Hanya untuk membangun ide @ SCB (yang persis seperti yang saya butuhkan) untuk membuat subkelas KillableThread dengan fungsi khusus:
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.
sumber
Seperti disebutkan dalam jawaban @ Kozyarchuk , menginstal jejak berfungsi. Karena jawaban ini tidak mengandung kode, berikut adalah contoh siap pakai yang berfungsi:
Itu berhenti setelah dicetak
1
dan2
.3
tidak dicetak.sumber
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.
sumber
Mulai sub utas dengan setDaemon (True).
sumber
Inilah cara melakukannya:
Berikan beberapa detik, maka utas Anda harus dihentikan. Periksa juga
thread._Thread__delete()
metodenya.Saya akan merekomendasikan
thread.quit()
metode untuk kenyamanan. Misalnya, jika Anda memiliki soket di utas Anda, saya sarankan membuatquit()
metode di kelas pegangan-soket Anda, mengakhiri soket, kemudian jalankan bagianthread._Thread__stop()
dalam Andaquit()
.sumber
_Thread__stop()
hanya menandai utas sebagai terhenti , itu tidak benar-benar menghentikan utas! Tidak pernah melakukan ini. Baca .Jika Anda benar-benar membutuhkan kemampuan untuk membunuh sub-tugas, gunakan implementasi alternatif.
multiprocessing
dangevent
keduanya 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.
sumber