本文将要介绍使用以下六种方法来终止陷入死循环的线程:
- Set stop flag
- Raising exceptions
- Set daemon
- Traces
- Hidden function_stopper()
- Compullsory kill
1. Set stop flag
最直觉的方法就是设定一个stop flag,当stop flag为True时结束该线程,有两种实现方式
- 在自定义MyThread中实现计数器,并重构run函数
- 将stop_flag参数传入thread中
首先来建立一个死循环infinite_loop()
def infinite_loop()
while True:
print("run infinite loop...")
time.sleep(1)
1.1 方法一:在自定义MyThread中实现计数器,并重构run函数
定义cal_count()函数,当counter为3时 ,将stop_flag设为True,在run()会收到该stop_flag状态。为确保取得状态时不会被修改到,使用lock来将其锁定住。
import threading
import time
class MyThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.counter = 0
self.stop_flag = False
def run(self):
lock.acquire()
self.stop_flag = self.cal_count()
lock.release()
def cal_count(self):
while True:
self.counter += 1
time.sleep(1)
if self.counter == 3:
stop_flag = True
return stop_flag
修改一下infinite_loop()函数
def infinite_loop():
thread1 = MyThread()
thread1.start()
while not thread1.stop_flag:
print("run infinite loop...")
time.sleep(1)
thread1.join()
如此一来就可以终止进入死循环的线程了。
if __name__ == "__main__":
global lock
lock = threading.Lock()
infinite_loop()
print("Done.")
# ====== output ======
run infinite loop...
run infinite loop...
run infinite loop...
run infinite loop...
Done.
1.2 方法二:将stop_flag参数传入thread中
定义一个thread_job线程,输入参数为stop_flag,当stop_flag为False则结束该循环。
import threading
import time
def thread_job(stop_flag):
# infinite_loop
while True:
print("run infinite loop...")
time.sleep(1)
if stop_flag():
break
使用threading.Thread(target=thread_job)建立一个线程,输入参数stop_flag,并用start开始执行。
当counter为3时,将stop_flag设为True,同时thread1线程就会终止。
if __name__ == "__main__":
counter = 0
stop_flag = False
thread1 = threading.Thread(target=thread_job, args=(lambda :stop_flag,))
thread1.start()
while True:
counter += 1
time.sleep(1)
if counter == 3:
stop_flag = True
break
thread1.join()
print("Done!")
2. Raising exception
在自定义MyThread中建立raise_exception()函数,并使用PyThreadState_SetAsyncExc()在线程中引发异常。
重构run(),当counter为3时,触发raise_exception()。
import threading
import time
import ctypes
class MyThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.counter = 0
def run(self):
# target function of the thread class
while True:
print("run infinite loop...")
self.counter += 1
time.sleep(1)
if self.counter == 3:
self.raise_exception()
def get_id(self):
# returns id of the respective thread
if hasattr(self, '_thread_id'):
return self._thread_id
for id, thread in threading._active.items():
if thread is self:
return id
def raise_exception(self):
thread_id = self.get_id()
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id,
ctypes.py_object(SystemExit))
if res > 1:
ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id, 0)
print('Exception raise failure')
if __name__ == '__main__':
thread1 = MyThread()
thread1.start()
thread1.join()
print("Done.")
3.Set daemon
将线程中的daemon参数设定为True,则主程序退出,该线程也会跟着结束。
import threading
import time
import sys
def thread_job():
# infinite_loop
while True:
print("run infinite loop...")
time.sleep(1)
if __name__ == '__main__':
counter = 0
thread1 = threading.Thread(target = thread_job)
thread1.daemon = True
thread1.start()
while True:
counter += 1
time.sleep(1)
if counter == 3:
sys.exit()
4. Traces
在自定义MyThread中加入traces追踪,当检测到kill flag为True,就会引发SystemExit()异常来结束线程。
class MyThread(threading.Thread):
def __init__(self, *args, **keywords):
threading.Thread.__init__(self, *args, **keywords)
self.killed = False
def start(self):
self.__run_backup = self.run
self.run = self.__run
threading.Thread.start(self)
def __run(self):
sys.settrace(self.globaltrace)
self.__run_backup()
self.run = self.__run_backup
def globaltrace(self, frame, event, arg):
if event == "call":
return self.localtrace
else:
return None
def localtrace(self, frame, event, arg):
if self.killed:
if event=="line":
raise SystemExit()
return self.localtrace
def kill(self):
self.killed = True
接着分为三个步骤:
- 建立一个线程thread1,输入target为infinite_loop()函数,并用start开始执行。
- 建立第二个线程thread2,任务为实现一个计数器,当counter为3时结束。
- 确认线程thread2的状态,当thread2结束则将thread1 killed掉。
def infinite_loop():
# first thread job
while True:
print("run infinite loop")
time.sleep(1)
def second_thread_job():
counter = 0
while True:
counter += 1
time.sleep(1)
if counter == 3:
return
if __name__ == "__main__":
thread1 = MyThread(target=second_thread_job)
thread1.start()
thread2 = threading.Thread(target=second_thread_job)
thread2.start()
thread2.join()
if not thread2.is_alive():
thread1.kill()
thread1.join()
print("Done,")
5. Hidden function_stopper()
在自定义MyThread中设置threading.Event()作为Hidde function,并使用set()设置信号、is_set()确认目前信号状态。当其信号设置状态为True时,则结束该线程。
class MyThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self._stopper = threading.Event()
def stop_it(self):
self._stopper.set()
def stopped(self):
return self._stopper.is_set()
def run(self):
while not self.stopped():
print("run infinite loop...")
time.sleep(1)
接着一样执行三个步骤:
- 建立一个线程thread1,并用start开始执行
- 建立第二个线程thread2,任务为实现一个计数器,当counter为3时结束
- 确认执行线程thread2的状态,当thread2结束则将thread1停止
def second_thread_job():
counter = 0
while True:
counter += 1
time,sleep(1)
if counter==3:
return
if __name__=="__main__":
thread1 = MyThread()
thread1.start()
thread2 = threading.Thread(target=second_thread_job)
thread2.start()
thread2.join()
if not thread2.is_alive():
thread1.stop_it()
thread1.join()
print("Done.")
6. Compulsory kill
若使用各种方法都还是无法将线程终止,最差的方法就是将其强制kill
import threading
import time
import ctypes
import inspect
def _async_raise(tid, exctype):
"""raises the exception, performs cleanup if needed"""
tid = ctypes.c_long(tid)
if not inspect.isclass(exctype):
exctype = type(exctype)
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(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(tid, None)
raise SystemError("PyThreadState_SetAsyncExc failed")
def stop_thread(thread):
_async_raise(thread.ident, SystemExit)
接着同样执行三个步骤:
- 建立一个线程thread1,输入target为infinite_loop()函数,并用start开始执行
- 建立第二个线程thread2,任务为实现一个计数器,当counter为3时结束
- 确认线程thread2的状态,当thread2结束则将thread1 kill掉
def infinite_loop():
# first thread job
while True:
print(“run infinite loop…”)
time.sleep(1)
def second_thread_job():
counter = 0
while True:
counter += 1
time.sleep(1)
if counter == 3:
return
if __name__ == ‘__main__’:
thread1 = MyThread(target = infinite_loop)
thread1.start()
thread2 = threading.Thread(target = second_thread_job)
thread2.start()
thread2.join()
if not thread2.is_alive():
stop_thread(thread1)
thread1.join()
print("Done.")