Apakah mungkin untuk menghentikan thread yang sedang berjalan tanpa mengatur / memeriksa flag / semaphores / etc?
Apakah mungkin untuk menghentikan thread yang sedang berjalan tanpa mengatur / memeriksa flag / semaphores / etc?
Jawaban:
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 menghindari Address already in use
kesalahan.
None
alih-alih 0
untuk res != 1
kasus ini, dan saya harus memanggil ctypes.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.
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
dengan multiprocessing.Process
dan semua queue.Queue
dengan multiprocessing.Queue
dan tambahkan panggilan yang diperlukan p.terminate()
untuk proses induk Anda yang ingin membunuh anaknyap
multiprocessing
itu 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
.
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 digunakan QueueHandler
(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
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)
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.
import ctypes
def terminate_thread(thread):
"""Terminates a python thread from another thread.
:param thread: a threading.Thread instance
"""
if not thread.isAlive():
return
exc = ctypes.py_object(SystemExit)
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(
ctypes.c_long(thread.ident), exc)
if res == 0:
raise ValueError("nonexistent thread id")
elif res > 1:
# """if it returns a number greater than one, you're in trouble,
# and you should call it again with exc=NULL to revert the effect"""
ctypes.pythonapi.PyThreadState_SetAsyncExc(thread.ident, None)
raise SystemError("PyThreadState_SetAsyncExc failed")
KeyboardInterrupt
sehingga mereka memiliki kesempatan untuk membersihkan. Jika mereka MASIH menggantung setelah itu, maka SystemExit
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.
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.Event
dan multiprocessing.Semaphore
kerja sama persis dengan cara threading.Event
dan threading.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 :
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 daemon
sebelum start()
metode dipanggil!
Tentu saja Anda dapat, dan harus, menggunakannya daemon
bahkan 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.
Anda dapat membunuh utas dengan menginstal jejak ke utas yang akan keluar utas. Lihat tautan terlampir untuk satu kemungkinan implementasi.
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. .
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 ...
Sangat mungkin untuk menerapkan Thread.stop
metode 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 Thread3
kelas muncul untuk menjalankan kode sekitar 33% lebih cepat dari Thread2
kelas.
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 yang sys.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.
from ctypes import *
pthread = cdll.LoadLibrary("libpthread-2.15.so")
pthread.pthread_cancel(c_ulong(t.ident))
t adalah Thread
objekmu.
Baca sumber python ( Modules/threadmodule.c
dan Python/thread_pthread.h
) yang bisa Anda lihat Thread.ident
adalah pthread_t
tipe, jadi Anda bisa melakukan apa saja yang pthread
bisa dilakukan dalam penggunaan python libpthread
.
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.
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]
SystemExit
pada after_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:
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).
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.
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.
- Misalnya: t1.raise_exc (TypeError) dan bukan t1.raise_exc (TypeError ("blah")).
- IMHO itu bug, dan saya melaporkannya sebagai satu. Untuk info lebih lanjut, http://mail.python.org/pipermail/python-dev/2006-August/068158.html
- 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.
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.
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.
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.
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 1
dan 2
. 3
tidak dicetak.
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;
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
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()
.
_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
dan gevent
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.