เป็นไปได้ไหมที่จะยกเลิกเธรดที่กำลังรันอยู่โดยไม่ต้องตั้งค่า / ตรวจสอบการตั้งค่าสถานะ / เซมาฟอร์ / etc ใด ๆ ?
เป็นไปได้ไหมที่จะยกเลิกเธรดที่กำลังรันอยู่โดยไม่ต้องตั้งค่า / ตรวจสอบการตั้งค่าสถานะ / เซมาฟอร์ / etc ใด ๆ ?
คำตอบ:
โดยทั่วไปแล้วมันเป็นรูปแบบที่ไม่ดีในการฆ่าเธรดโดยทันทีใน Python และในภาษาใด ๆ นึกถึงกรณีต่อไปนี้:
วิธีที่ดีในการจัดการสิ่งนี้หากคุณสามารถจ่ายได้ (ถ้าคุณจัดการเธรดของคุณเอง) คือการมีการตั้งค่าสถานะ exit_request ที่แต่ละกระทู้ตรวจสอบในช่วงเวลาปกติเพื่อดูว่าถึงเวลาที่จะออก
ตัวอย่างเช่น:
import threading
class StoppableThread(threading.Thread):
"""Thread class with a stop() method. The thread itself has to check
regularly for the stopped() condition."""
def __init__(self, *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()
ในรหัสนี้คุณควรจะเรียกในหัวข้อเมื่อคุณต้องการให้ออกและรอด้ายเพื่อออกอย่างถูกต้องโดยใช้stop()
join()
เธรดควรตรวจสอบการตั้งค่าสถานะการหยุดในช่วงเวลาปกติ
มีหลายกรณี แต่เมื่อคุณต้องการฆ่าเธรด ตัวอย่างคือเมื่อคุณกำลังตัดไลบรารีภายนอกที่ไม่ว่างสำหรับการโทรนานและคุณต้องการขัดจังหวะ
รหัสต่อไปนี้อนุญาตให้ (มีข้อ จำกัด บางอย่าง) ยกข้อยกเว้นในเธรด Python:
def _async_raise(tid, exctype):
'''Raises an exception in the threads with id tid'''
if not inspect.isclass(exctype):
raise TypeError("Only types can be raised (not instances)")
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid),
ctypes.py_object(exctype))
if res == 0:
raise ValueError("invalid thread id")
elif res != 1:
# "if it returns a number greater than one, you're in trouble,
# and you should call it again with exc=NULL to revert the effect"
ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid), None)
raise SystemError("PyThreadState_SetAsyncExc failed")
class ThreadWithExc(threading.Thread):
'''A thread class that supports raising exception in the thread from
another thread.
'''
def _get_my_tid(self):
"""determines this (self's) thread id
CAREFUL : this function is executed in the context of the caller
thread, to get the identity of the thread represented by this
instance.
"""
if not self.isAlive():
raise threading.ThreadError("the thread is not active")
# do we have it cached?
if hasattr(self, "_thread_id"):
return self._thread_id
# no, look for it in the _active dict
for tid, tobj in threading._active.items():
if tobj is self:
self._thread_id = tid
return tid
# TODO: in python 2.6, there's a simpler way to do : self.ident
raise AssertionError("could not determine the thread's id")
def raiseExc(self, exctype):
"""Raises the given exception type in the context of this thread.
If the thread is busy in a system call (time.sleep(),
socket.accept(), ...), the exception is simply ignored.
If you are sure that your exception should terminate the thread,
one way to ensure that it works is:
t = ThreadWithExc( ... )
...
t.raiseExc( SomeException )
while t.isAlive():
time.sleep( 0.1 )
t.raiseExc( SomeException )
If the exception is to be caught by the thread, you need a way to
check that your thread has caught it.
CAREFUL : this function is executed in the context of the
caller thread, to raise an excpetion in the context of the
thread represented by this instance.
"""
_async_raise( self._get_my_tid(), exctype )
(อ้างอิงจากKillable Threadsโดย Tomer Filiba ข้อความอ้างอิงเกี่ยวกับค่าส่งคืนของที่PyThreadState_SetAsyncExc
ปรากฏมาจากPython เวอร์ชั่นเก่า )
ดังที่ระบุไว้ในเอกสารประกอบสิ่งนี้ไม่ใช่กระสุนวิเศษเพราะถ้าเธรดไม่ว่างอยู่นอกตัวแปล Python มันจะไม่หยุดชะงัก
รูปแบบการใช้งานที่ดีของโค้ดนี้คือให้เธรดตรวจจับข้อยกเว้นเฉพาะและทำการล้างข้อมูล ด้วยวิธีนี้คุณสามารถขัดจังหวะงานและยังคงมีการล้างข้อมูลที่เหมาะสม
SO_REUSEADDR
ซ็อกเก็ตตัวเลือกเพื่อหลีกเลี่ยงAddress already in use
ข้อผิดพลาด
None
แทน0
สำหรับres != 1
กรณีและฉันต้องโทรctypes.c_long(tid)
แล้วส่งสิ่งนั้นไปยังฟังก์ชัน ctypes ใด ๆ แทนที่จะเป็น tid โดยตรง
ไม่มี API อย่างเป็นทางการที่จะทำเช่นนั้นไม่ใช่
คุณต้องใช้แพลตฟอร์ม API เพื่อฆ่าเธรดเช่น pthread_kill หรือ TerminateThread คุณสามารถเข้าถึง API เช่นผ่าน pythonwin หรือผ่าน ctypes
ขอให้สังเกตว่านี่ไม่ปลอดภัยโดยเนื้อแท้ มันอาจจะนำไปสู่ขยะที่ไม่สามารถเก็บได้ (จากตัวแปรท้องถิ่นของสแต็กเฟรมที่กลายเป็นขยะ) และอาจนำไปสู่การหยุดชะงักหากเธรดที่ถูกฆ่ามี GIL ที่จุดเมื่อมันถูกฆ่า
multiprocessing.Process
สามารถp.terminate()
ในกรณีที่ฉันต้องการฆ่าเธรด แต่ไม่ต้องการใช้แฟล็ก / ล็อก / สัญญาณ / เซมาฟอเรส / เหตุการณ์ / อะไรก็ตามฉันโปรโมตเธรดไปยังกระบวนการเป่าเต็ม สำหรับรหัสที่ใช้เพียงไม่กี่เธรดค่าใช้จ่ายจะไม่เลว
เช่นนี้มีประโยชน์ในการยกเลิก "เธรด" ของผู้ช่วยเหลือที่เรียกใช้การบล็อก I / O อย่างง่ายดาย
การแปลงเป็นเรื่องเล็กน้อย: ในรหัสที่เกี่ยวข้องแทนที่ทั้งหมดthreading.Thread
ด้วยmultiprocessing.Process
และทั้งหมดqueue.Queue
ด้วยmultiprocessing.Queue
และเพิ่มการเรียกร้องp.terminate()
ให้กระบวนการแม่ของคุณที่ต้องการฆ่าลูกของมันp
ดูเอกสารงูหลามmultiprocessing
multiprocessing
เป็นสิ่งที่ดี แต่โปรดทราบว่าข้อโต้แย้งนั้นได้ถูกดองไว้กับกระบวนการใหม่ ดังนั้นถ้าหนึ่งของการขัดแย้งเป็นสิ่งที่ไม่ pickable (เช่นlogging.log
) multiprocessing
มันอาจจะไม่เป็นความคิดที่ดีที่จะใช้
multiprocessing
อาร์กิวเมนต์จะถูกดองในกระบวนการใหม่บน Windows แต่ Linux ใช้การฟอร์กเพื่อคัดลอก (Python 3.7 ไม่แน่ใจว่าเวอร์ชันอื่น ๆ ) ดังนั้นคุณจะจบลงด้วยรหัสที่ทำงานบน Linux แต่เกิดข้อผิดพลาดที่เกิดขึ้นบน Windows
multiprocessing
ด้วยการบันทึกเป็นธุรกิจที่ยุ่งยาก จำเป็นต้องใช้QueueHandler
(ดูบทช่วยสอนนี้ ) ฉันเรียนรู้มันอย่างหนัก
หากคุณพยายามที่จะยุติโปรแกรมทั้งหมดคุณสามารถตั้งกระทู้เป็น "daemon" เห็น Thread.daemon
ตามที่คนอื่น ๆ ได้กล่าวไว้บรรทัดฐานคือการตั้งค่าสถานะหยุด สำหรับบางสิ่งบางอย่างที่มีน้ำหนักเบา (ไม่มีซับคลาสของเธรดไม่มีตัวแปรโกลบอล) การเรียกกลับแลมบ์ดาเป็นตัวเลือก (หมายเหตุเครื่องหมายวงเล็บในif stop()
)
import threading
import time
def do_work(id, stop):
print("I am thread", id)
while True:
print("I am thread {} doing something".format(id))
if stop():
print(" Exiting loop.")
break
print("Thread {}, signing off".format(id))
def main():
stop_threads = False
workers = []
for id in range(0,3):
tmp = threading.Thread(target=do_work, args=(id, lambda: stop_threads))
workers.append(tmp)
tmp.start()
time.sleep(3)
print('main: done sleeping; time to stop the threads.')
stop_threads = True
for worker in workers:
worker.join()
print('Finis.')
if __name__ == '__main__':
main()
การแทนที่print()
ด้วยpr()
ฟังก์ชั่นที่ฟลัชเสมอ ( sys.stdout.flush()
) อาจช่วยเพิ่มความแม่นยำของเอาต์พุตเชลล์
(ทดสอบเฉพาะบน Windows / Eclipse / Python3.3)
pr()
ฟังก์ชั่นคืออะไร?
สิ่งนี้ขึ้นอยู่กับthread2 - เธรดที่สามารถฆ่าได้ (สูตร Python)
คุณต้องโทร PyThreadState_SetasyncExc () ซึ่งมีให้บริการผ่าน ctypes เท่านั้น
สิ่งนี้ได้รับการทดสอบบน Python 2.7.3 เท่านั้น แต่มีแนวโน้มว่าจะทำงานกับรุ่น 2.x ล่าสุดอื่น ๆ
import ctypes
def terminate_thread(thread):
"""Terminates a python thread from another thread.
:param thread: a threading.Thread instance
"""
if not thread.isAlive():
return
exc = ctypes.py_object(SystemExit)
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(
ctypes.c_long(thread.ident), exc)
if res == 0:
raise ValueError("nonexistent thread id")
elif res > 1:
# """if it returns a number greater than one, you're in trouble,
# and you should call it again with exc=NULL to revert the effect"""
ctypes.pythonapi.PyThreadState_SetAsyncExc(thread.ident, None)
raise SystemError("PyThreadState_SetAsyncExc failed")
KeyboardInterrupt
เพื่อให้พวกเขามีโอกาสทำความสะอาด หากพวกเขายังคงแขวนอยู่หลังจากนั้นก็SystemExit
มีความเหมาะสมหรือเพียงแค่ฆ่ากระบวนการจากสถานี
pthread_cleanup_push()/_pop()
แต่ก็มีหลายงานที่ต้องใช้อย่างถูกต้องและมันจะทำให้ล่ามช้าลงอย่างเห็นได้ชัด
คุณไม่ควรบังคับให้ฆ่าเธรดโดยไม่ให้ความร่วมมือ
การฆ่าเธรดจะเป็นการลบการรับประกันใด ๆ ที่ลอง / ในที่สุดบล็อกการตั้งค่าดังนั้นคุณอาจปล่อยให้ล็อคถูกล็อกเปิดไฟล์ ฯลฯ
ครั้งเดียวที่คุณสามารถยืนยันได้ว่าการบังคับให้เธรดฆ่าเป็นความคิดที่ดีคือการฆ่าโปรแกรมอย่างรวดเร็ว แต่ไม่ควรทำเธรดเดี่ยว
ใน Python คุณไม่สามารถฆ่าเธรดได้โดยตรง
หากคุณไม่ได้จริงๆต้องมีกระทู้ (!) สิ่งที่คุณสามารถทำแทนการใช้เกลียวแพคเกจคือการใช้ multiprocessingแพคเกจ ที่นี่เพื่อฆ่ากระบวนการคุณสามารถเรียกวิธีการได้ดังนี้
yourProcess.terminate() # kill the process!
Python จะฆ่าโพรเซสของคุณ (บน Unix ผ่านสัญญาณ SIGTERM ในขณะที่ใช้ Windows ผ่านการTerminateProcess()
โทร) ให้ความสนใจที่จะใช้มันในขณะที่ใช้คิวหรือท่อ! (อาจทำให้ข้อมูลใน Queue / Pipe เสียหายได้)
โปรดทราบว่าmultiprocessing.Event
และการmultiprocessing.Semaphore
ทำงานในลักษณะเดียวกันกับthreading.Event
และthreading.Semaphore
ตามลำดับ ในความเป็นจริงคนแรกคือโคลนของ latters
หากคุณต้องการใช้เธรดจริงๆจะไม่มีวิธีฆ่าโดยตรง คุณสามารถทำอะไร แต่คือการใช้"ด้ายภูต" อันที่จริงแล้วใน Python เธรดสามารถถูกแฟล็กเป็นdaemon :
yourThread.daemon = True # set the Thread as a "daemon thread"
โปรแกรมหลักจะออกเมื่อไม่มีเธรด daemon ที่ยังมีชีวิตอยู่เหลืออยู่ กล่าวอีกนัยหนึ่งเมื่อเธรดหลักของคุณ (ซึ่งเป็นเธรดที่ไม่ใช่ daemon) จะเสร็จสิ้นการดำเนินการโปรแกรมจะออกจากโปรแกรมแม้ว่าจะยังมี daemon เธรดบางตัวทำงานอยู่
โปรดทราบว่ามีความจำเป็นต้องตั้งกระทู้เป็นdaemon
ก่อนstart()
วิธีการที่เรียกว่า!
แน่นอนคุณสามารถและควรจะใช้แม้จะมีdaemon
multiprocessing
ที่นี่เมื่อกระบวนการหลักจบการทำงานจะพยายามยุติกระบวนการลูกที่เป็นแบบภูต
ในที่สุดโปรดทราบว่าsys.exit()
และos.kill()
ไม่ใช่ตัวเลือก
คุณสามารถฆ่าเธรดโดยติดตั้งการติดตามลงในเธรดที่จะออกจากเธรด ดูลิงค์ที่แนบมาเพื่อการใช้งานที่เป็นไปได้
หากคุณกำลังอย่างชัดเจนเรียกtime.sleep()
ว่าเป็นส่วนหนึ่งของด้าย (พูดเลือกตั้งบางบริการภายนอกของคุณ) การปรับปรุงเมื่อวิธี Phillipe คือการใช้หมดเวลาในevent
's wait()
วิธีการใดก็ตามที่คุณsleep()
ตัวอย่างเช่น:
import threading
class KillableThread(threading.Thread):
def __init__(self, sleep_interval=1):
super().__init__()
self._kill = threading.Event()
self._interval = sleep_interval
def run(self):
while True:
print("Do Something")
# If no kill signal is set, sleep for the interval,
# If kill signal comes in while sleeping, immediately
# wake up and handle
is_killed = self._kill.wait(self._interval)
if is_killed:
break
print("Killing Thread")
def kill(self):
self._kill.set()
จากนั้นให้เรียกใช้
t = KillableThread(sleep_interval=5)
t.start()
# Every 5 seconds it prints:
#: Do Something
t.kill()
#: Killing Thread
ข้อดีของการใช้งานwait()
แทนการใช้sleep()
ไอเอ็นจีและตรวจสอบเหตุการณ์เป็นประจำคือคุณสามารถตั้งโปรแกรมในช่วงเวลาที่นานขึ้นของเธรดได้เธรดจะหยุดลงเกือบจะในทันที (เมื่อคุณต้องการsleep()
ไอเอ็นจี) และในความคิดของฉัน .
มันจะดีกว่าถ้าคุณไม่ฆ่าเธรด วิธีหนึ่งอาจแนะนำบล็อก "ลอง" ในวงรอบของเธรดและเพื่อยกเว้นเมื่อคุณต้องการหยุดเธรด (ตัวอย่างเช่นตัวแบ่ง / return / ... ที่หยุดสำหรับ / ในขณะที่ ... ฉันเคยใช้สิ่งนี้กับแอพของฉันและใช้งานได้ ...
แน่นอนเป็นไปได้ที่จะใช้Thread.stop
วิธีการตามที่แสดงในตัวอย่างรหัสต่อไปนี้:
import sys
import threading
import time
class StopThread(StopIteration):
pass
threading.SystemExit = SystemExit, StopThread
class Thread2(threading.Thread):
def stop(self):
self.__stop = True
def _bootstrap(self):
if threading._trace_hook is not None:
raise ValueError('Cannot run thread with tracing!')
self.__stop = False
sys.settrace(self.__trace)
super()._bootstrap()
def __trace(self, frame, event, arg):
if self.__stop:
raise StopThread()
return self.__trace
class Thread3(threading.Thread):
def _bootstrap(self, stop_thread=False):
def stop():
nonlocal stop_thread
stop_thread = True
self.stop = stop
def tracer(*_):
if stop_thread:
raise StopThread()
return tracer
sys.settrace(tracer)
super()._bootstrap()
###############################################################################
def main():
test1 = Thread2(target=printer)
test1.start()
time.sleep(1)
test1.stop()
test1.join()
test2 = Thread2(target=speed_test)
test2.start()
time.sleep(1)
test2.stop()
test2.join()
test3 = Thread3(target=speed_test)
test3.start()
time.sleep(1)
test3.stop()
test3.join()
def printer():
while True:
print(time.time() % 1)
time.sleep(0.1)
def speed_test(count=0):
try:
while True:
count += 1
except StopThread:
print('Count =', count)
if __name__ == '__main__':
main()
Thread3
ชั้นเรียนจะปรากฏรหัสวิ่งประมาณ 33% เร็วกว่าThread2
ระดับ
self.__stop
การตั้งค่าในเธรด โปรดทราบว่าเหมือนกับโซลูชันอื่น ๆ ส่วนใหญ่ที่นี่จะไม่ขัดจังหวะการโทรบล็อกเนื่องจากฟังก์ชันการติดตามจะได้รับการโทรเมื่อมีการป้อนขอบเขตภายในเครื่องใหม่เท่านั้น สิ่งที่ควรสังเกตก็คือsys.settrace
ความหมายที่แท้จริงสำหรับการใช้งาน debuggers โปรไฟล์ ฯลฯ และถือว่าเป็นรายละเอียดการใช้งานของ CPython และไม่รับประกันว่าจะมีอยู่ในการใช้งาน Python อื่น ๆ
Thread2
คลาสคือมันรันโค้ดช้ากว่าประมาณสิบเท่า บางคนอาจพบว่ายอมรับได้
from ctypes import *
pthread = cdll.LoadLibrary("libpthread-2.15.so")
pthread.pthread_cancel(c_ulong(t.ident))
tคือThread
วัตถุของคุณ
อ่านแหล่งไพ ธ อน ( Modules/threadmodule.c
และPython/thread_pthread.h
) ที่คุณเห็นว่าThread.ident
เป็นpthread_t
ประเภทดังนั้นคุณสามารถทำสิ่งใดก็ได้pthread
ในการใช้งานไพlibpthread
ธ อน
วิธีแก้ปัญหาต่อไปนี้สามารถใช้เพื่อฆ่าเธรด:
kill_threads = False
def doSomething():
global kill_threads
while True:
if kill_threads:
thread.exit()
......
......
thread.start_new_thread(doSomething, ())
สิ่งนี้สามารถใช้ได้แม้กระทั่งการยกเลิกเธรดซึ่งมีการเขียนโค้ดในโมดูลอื่นจากเธรดหลัก เราสามารถประกาศตัวแปรโกลบอลในโมดูลนั้นและใช้มันเพื่อยุติ thread / s ที่เกิดในโมดูลนั้น
ฉันมักจะใช้สิ่งนี้เพื่อยุติกระทู้ทั้งหมดที่ออกจากโปรแกรม นี่อาจไม่ใช่วิธีที่สมบูรณ์แบบในการยุติเธรด แต่สามารถช่วยได้
ฉันมาสายไปที่เกมนี้ แต่ฉันได้ต่อสู้กับคำถามที่คล้ายกันและต่อไปนี้ปรากฏขึ้นเพื่อแก้ไขปัญหาได้อย่างสมบูรณ์แบบสำหรับฉันและให้ฉันทำการตรวจสอบสถานะเธรดพื้นฐานและล้างข้อมูลเมื่อออกจากเธรดย่อย daemonized:
import threading
import time
import atexit
def do_work():
i = 0
@atexit.register
def goodbye():
print ("'CLEANLY' kill sub-thread with value: %s [THREAD: %s]" %
(i, threading.currentThread().ident))
while True:
print i
i += 1
time.sleep(1)
t = threading.Thread(target=do_work)
t.daemon = True
t.start()
def after_timeout():
print "KILL MAIN THREAD: %s" % threading.currentThread().ident
raise SystemExit
threading.Timer(2, after_timeout).start()
อัตราผลตอบแทน:
0
1
KILL MAIN THREAD: 140013208254208
'CLEANLY' kill sub-thread with value: 2 [THREAD: 140013674317568]
SystemExit
ในafter_timeout
เธรดจึงทำทุกอย่างกับเธรดหลัก (ซึ่งเป็นเพียงการรออดีตเพื่อออกในตัวอย่างนี้)
SystemExit
มีคุณสมบัติพิเศษเพียงสองประการเท่านั้น: จะไม่สร้างการติดตามย้อนกลับ(เมื่อเธรดใด ๆ ออกจากการขว้างหนึ่ง) และหากเธรดหลักออกจากการขว้างด้วยการโยนมันจะกำหนดสถานะการออก (ในขณะที่รอเธรด non-daemon อื่น ๆ เพื่อออก)
สิ่งหนึ่งที่ผมต้องการที่จะเพิ่มคือว่าถ้าคุณอ่านเอกสารอย่างเป็นทางการในเกลียว lib หลามก็ขอแนะนำให้หลีกเลี่ยงการใช้งานของ "ปีศาจหัวข้อ" เมื่อคุณไม่ต้องการให้หัวข้อสิ้นสุดทันทีที่มีธงที่เปาโล Rovelli กล่าวถึง
จากเอกสารอย่างเป็นทางการ:
เธรด Daemon หยุดทำงานอย่างกะทันหันเมื่อปิดระบบ ทรัพยากรของพวกเขา (เช่นไฟล์ที่เปิดธุรกรรมฐานข้อมูล ฯลฯ ) อาจไม่ได้รับการเผยแพร่อย่างถูกต้อง ถ้าคุณต้องการให้เธรดของคุณหยุดอย่างเรียบร้อยทำให้พวกมันไม่ใช่ daemonic และใช้กลไกการส่งสัญญาณที่เหมาะสมเช่น Event
ฉันคิดว่าการสร้าง daemonic threads ขึ้นอยู่กับแอปพลิเคชันของคุณ แต่โดยทั่วไป (และในความเห็นของฉัน) จะเป็นการดีกว่าที่จะหลีกเลี่ยงการฆ่าพวกเขาหรือทำให้พวกเขาเป็น daemonic ในการประมวลผลหลายตัวคุณสามารถใช้is_alive()
เพื่อตรวจสอบสถานะกระบวนการและ "ยุติ" เพื่อทำให้เสร็จ (นอกจากนี้คุณยังหลีกเลี่ยงปัญหา GIL) แต่คุณสามารถพบปัญหาเพิ่มเติมบางครั้งเมื่อคุณเรียกใช้รหัสใน Windows
และโปรดจำไว้เสมอว่าหากคุณมี "กระทู้สด" ตัวแปลภาษา Python จะทำงานเพื่อรอข้อความนั้น (เพราะภูตนี้สามารถช่วยคุณได้ถ้าไม่จบลงอย่างกะทันหัน)
แม้ว่าจะค่อนข้างเก่า แต่นี่อาจเป็นคำตอบที่สะดวกสำหรับบางคน:
โมดูลเล็ก ๆ ที่ขยายฟังก์ชั่นโมดูลของเธรดช่วยให้เธรดหนึ่งยกข้อยกเว้นในบริบทของเธรดอื่น ในการเพิ่ม
SystemExit
คุณสามารถฆ่าเธรดงูหลามได้ในที่สุด
import threading
import ctypes
def _async_raise(tid, excobj):
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(excobj))
if res == 0:
raise ValueError("nonexistent thread id")
elif res > 1:
# """if it returns a number greater than one, you're in trouble,
# and you should call it again with exc=NULL to revert the effect"""
ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0)
raise SystemError("PyThreadState_SetAsyncExc failed")
class Thread(threading.Thread):
def raise_exc(self, excobj):
assert self.isAlive(), "thread must be started"
for tid, tobj in threading._active.items():
if tobj is self:
_async_raise(tid, excobj)
return
# the thread was alive when we entered the loop, but was not found
# in the dict, hence it must have been already terminated. should we raise
# an exception here? silently ignore?
def terminate(self):
# must raise the SystemExit type, instead of a SystemExit() instance
# due to a bug in PyThreadState_SetAsyncExc
self.raise_exc(SystemExit)
ดังนั้นจึงอนุญาตให้ "เธรดยกข้อยกเว้นในบริบทของเธรดอื่น" และด้วยวิธีนี้เธรดที่สิ้นสุดสามารถจัดการการเลิกจ้างโดยไม่ต้องตรวจสอบการตั้งค่าสถานะการยกเลิกเป็นประจำ
อย่างไรก็ตามตามแหล่งที่มาเดิมมีปัญหาบางอย่างกับรหัสนี้
- ข้อยกเว้นจะเพิ่มขึ้นเฉพาะเมื่อเรียกใช้งาน python bytecode หากเธรดของคุณเรียกใช้ฟังก์ชันการบล็อกเนทิฟ / บิวด์อินข้อยกเว้นจะเพิ่มขึ้นเฉพาะเมื่อการเรียกใช้งานกลับไปที่รหัสไพ ธ อน
- นอกจากนี้ยังมีปัญหาหากฟังก์ชันในตัวเรียก PyErr_Clear () ภายในซึ่งจะยกเลิกข้อยกเว้นที่รออนุมัติของคุณได้อย่างมีประสิทธิภาพ คุณสามารถลองเพิ่มอีกครั้ง
- สามารถยกประเภทยกเว้นได้อย่างปลอดภัยเท่านั้น อินสแตนซ์ของข้อยกเว้นมีแนวโน้มที่จะทำให้เกิดพฤติกรรมที่ไม่คาดคิดและถูก จำกัด
- ตัวอย่างเช่น: t1.raise_exc (TypeError) และไม่ใช่ t1.raise_exc (TypeError ("blah"))
- IMHO เป็นข้อผิดพลาดและฉันรายงานว่าเป็นหนึ่ง สำหรับข้อมูลเพิ่มเติม http://mail.python.org/pipermail/python-dev/2006-August/068158.html
- ฉันขอให้แสดงฟังก์ชั่นนี้ในโมดูลด้ายในตัว แต่เนื่องจาก ctypes ได้กลายเป็นห้องสมุดมาตรฐาน (จาก 2.5) และ
คุณลักษณะนี้ไม่น่าจะเป็นผู้ไม่เชื่อเรื่องการใช้งานจึงอาจ
ไม่ได้รับการเปิดเผย
ดูเหมือนว่าจะทำงานกับ pywin32 บน windows 7
my_thread = threading.Thread()
my_thread.start()
my_thread._Thread__stop()
Pieter Hintjens - หนึ่งในผู้ก่อตั้งØMQ - โครงการ - กล่าวว่าใช้ØMQและหลีกเลี่ยงการซิงโครไนซ์แบบดั้งเดิมเช่นการล็อก mutexes กิจกรรม ฯลฯ เป็นวิธีที่ปลอดภัยและปลอดภัยที่สุดในการเขียนโปรแกรมแบบมัลติเธรด:
http://zguide.zeromq.org/py:all#Multithreading-with-ZeroMQ
ซึ่งรวมถึงการบอกเธรดลูกว่าควรยกเลิกงาน สิ่งนี้จะทำได้โดยเตรียมเธรดด้วยØMQ-ซ็อกเก็ตและการสำรวจความเห็นบนซ็อกเก็ตนั้นสำหรับข้อความที่บอกว่าควรยกเลิก
ลิงค์นี้ยังมีตัวอย่างเกี่ยวกับรหัสหลามแบบมัลติเธรดด้วยØMQ
โดยที่คุณต้องการมีหลายเธรดของฟังก์ชั่นเดียวกันนี่คือ IMHO การใช้งานที่ง่ายที่สุดในการหยุดทีละไอดี:
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
สิ่งที่ดีอยู่ที่นี่คุณสามารถมีฟังก์ชั่นที่เหมือนกันและแตกต่างกันหลายอย่างและหยุดพวกเขาทั้งหมดด้วย functionname.stop
หากคุณต้องการมีเพียงหนึ่งเธรดของฟังก์ชันคุณไม่จำเป็นต้องจำรหัส แค่หยุดถ้าdoit.stop
> 0
เพียงสร้างความคิดของ @ SCB (ซึ่งเป็นสิ่งที่ฉันต้องการ) เพื่อสร้างคลาสย่อย KillableThread ด้วยฟังก์ชันที่กำหนดเอง:
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()
ตามธรรมชาติเช่นเดียวกับ @SBC เธรดไม่รอให้เรียกใช้การวนซ้ำใหม่เพื่อหยุด ในตัวอย่างนี้คุณจะเห็นข้อความ "เธรดฆ่า" พิมพ์หลังจาก "กำลังจะฆ่าเธรด" แทนที่จะรออีก 4 วินาทีเพื่อให้เธรดดำเนินการให้เสร็จสมบูรณ์ (เนื่องจากเรานอนหลับไปแล้ว 6 วินาที)
อาร์กิวเมนต์ที่สองใน Constructor KillableThread เป็นฟังก์ชันที่คุณกำหนดเอง (print_msg ที่นี่) อาร์กิวเมนต์ Args คืออาร์กิวเมนต์ที่จะใช้เมื่อเรียกใช้ฟังก์ชัน (("hello world")) ที่นี่
ตามที่ระบุไว้ในคำตอบของ @ Kozyarchuk การติดตั้งการติดตาม เนื่องจากคำตอบนี้ไม่มีรหัสนี่คือตัวอย่างที่พร้อมใช้งาน:
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
จะหยุดหลังจากที่มีการพิมพ์และ1
ไม่ได้พิมพ์2
3
คุณสามารถดำเนินการคำสั่งของคุณในกระบวนการแล้วฆ่ามันโดยใช้กระบวนการ ID ฉันต้องการซิงค์ระหว่างสองเธรดหนึ่งรายการซึ่งไม่ได้ส่งคืนด้วยตนเอง
processIds = []
def executeRecord(command):
print(command)
process = subprocess.Popen(command, stdout=subprocess.PIPE)
processIds.append(process.pid)
print(processIds[0])
#Command that doesn't return by itself
process.stdout.read().decode("utf-8")
return;
def recordThread(command, timeOut):
thread = Thread(target=executeRecord, args=(command,))
thread.start()
thread.join(timeOut)
os.kill(processIds.pop(), signal.SIGINT)
return;
เริ่มเธรดย่อยด้วย 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
นี่คือคำตอบที่ไม่ดีให้ดูความคิดเห็น
นี่คือวิธีการ:
from threading import *
...
for thread in enumerate():
if thread.isAlive():
try:
thread._Thread__stop()
except:
print(str(thread.getName()) + ' could not be terminated'))
ให้เวลาสองสามวินาทีจากนั้นเธรดของคุณจะหยุดทำงาน ตรวจสอบthread._Thread__delete()
วิธีการด้วย
ฉันขอแนะนำthread.quit()
วิธีการเพื่อความสะดวก ตัวอย่างเช่นถ้าคุณมีซ็อกเก็ตในหัวข้อของคุณผมขอแนะนำให้สร้างquit()
วิธีการในชั้นเรียนซ็อกเก็ตจับของคุณยุติซ็อกเก็ตแล้วทำงานภายในของคุณthread._Thread__stop()
quit()
_Thread__stop()
เพียงเครื่องหมายด้ายเป็นหยุดมันไม่จริงหยุดด้าย! อย่าทำอย่างนี้ ได้อ่าน
หากคุณต้องการความสามารถในการฆ่าภารกิจย่อยให้ใช้การดำเนินการทางเลือก multiprocessing
และgevent
ทั้งสองสนับสนุนการฆ่า "ด้าย" โดยไม่เจตนา
เกลียวของ Python ไม่รองรับการยกเลิก อย่าแม้แต่จะลอง รหัสของคุณมีแนวโน้มที่จะหยุดชะงักหน่วยความจำที่เสียหายหรือรั่วไหลหรือมีเอฟเฟกต์ยากต่อการดีบัก "น่าสนใจ" ที่ไม่ได้ตั้งใจซึ่งเกิดขึ้นได้ยากและไม่ได้กำหนดไว้ล่วงหน้า