多线程--终止线程的方法

本文将要介绍使用以下六种方法来终止陷入死循环的线程:

  • 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.")

参考目录

https://medium.com/ching-i/%E5%A4%9A%E5%9F%B7%E8%A1%8C%E7%B7%92-%E7%B5%82%E6%AD%A2%E5%9F%B7%E8%A1%8C%E7%B7%92%E7%9A%84%E6%96%B9%E6%B3%95-d9e50c180873

猜你喜欢

转载自blog.csdn.net/weixin_43229348/article/details/124141171