五種殺死子線程的方法

想看英文的可以點擊這裏


通常來說,突然殺死一個子線程往往不是一個好的選擇。但是,在某些特定的場合,我們需要殺死子線程。下面介紹了五種不同的方法殺死子線程:

  • Raising exceptions in a python thread
  • Set/Reset stop flag
  • Using traces to kill threads
  • Using the multiprocessing module to kill threads
  • Killing Python thread by setting it as daemon

1.Raising exceptions in a python thread

代碼如下:

# Python program raising 
# exceptions in a python 
# thread 

import threading 
import ctypes 
import time 

class thread_with_exception(threading.Thread): 
	def __init__(self, name): 
		threading.Thread.__init__(self) 
		self.name = name 
			
	def run(self): 

		# target function of the thread class 
		try: 
			while True: 
				print('running ' + self.name) 
		finally: 
			print('ended') 
		
	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') 
	
t1 = thread_with_exception('Thread 1') 
t1.start() 
time.sleep(2) 
t1.raise_exception() 
t1.join() 

當我們運行上面的代碼的時候,我們會發現,只要我們調用raise_exception()函數的時候,run()函數就會終止。這是因爲只要我們拋出異常的時候,程序就會跳轉到try代碼塊中,然後run()函數就會終止,之後join()函數就會被調用,然後殺死子線程。如果不調用raise_exception()函數的話,run()函數就會一直運行,然後join()函數也永遠不會調用去殺死子線程(這是因爲我們將run函數放在while循環中)。

2. Set/Reset stop flag

爲了殺死一個子線程,我們可以聲明一個停止標誌位,子線程會檢查這個標誌位是否爲True。比如:

# Python program showing 
# how to kill threads 
# using set/reset stop 
# flag 

import threading 
import time 

def run(): 
	while True: 
		print('thread running') 
		global stop_threads 
		if stop_threads: 
			break

stop_threads = False
t1 = threading.Thread(target = run) 
t1.start() 
time.sleep(1) 
stop_threads = True
t1.join() 
print('thread killed') 

在上面的代碼中,只要設置了全局變量stop_threads,目標函數run()就會停止運行,然後線程t1就可以通過t1.join()進行殺死。但是,由於某些特定的場所,我們會避免使用全局變量。在這些情況下,我們可以通過傳入一個函數對象實現相似的任務,正如下面所示:

# Python program killing 
# threads using stop 
# flag 

import threading 
import time 

def run(stop): 
	while True: 
		print('thread running') 
		if stop(): 
				break
				
def main(): 
		stop_threads = False
		t1 = threading.Thread(target = run, args =(lambda : stop_threads, )) 
		t1.start() 
		time.sleep(1) 
		stop_threads = True
		t1.join() 
		print('thread killed') 
main() 

在上面的代碼中,函數對象傳遞給了子線程,並且這個函數總是會返回局部變量stop_threads的值。在run()函數中,程序總是會檢查stop_threads的值,只要它被重置了,run()函數就會停止,然後子線程就會被殺死。

3. Using traces to kill threads

通過安裝traces,這個方法可以在每個線程中都有效。

# Python program using 
# traces to kill threads 

import sys 
import trace 
import threading 
import time 
class thread_with_trace(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

def func(): 
while True: 
	print('thread running') 

t1 = thread_with_trace(target = func) 
t1.start() 
time.sleep(2) 
t1.kill() 
t1.join() 
if not t1.isAlive(): 
print('thread killed') 

4. Using the multiprocessing module to kill threads

多進程模塊允許我們使用向創建一個子線程的方式來創建一個子進程。多進程模塊和多線程模塊的接口非常相似。舉個栗子,我們可以使用三個子線程來打印1-9的所有數字。

# Python program creating 
# three threads 

import threading 
import time 

# counts from 1 to 9 
def func(number): 
	for i in range(1, 10): 
		time.sleep(0.01) 
		print('Thread ' + str(number) + ': prints ' + str(number*i)) 

# creates 3 threads 
for i in range(0, 3): 
	thread = threading.Thread(target=func, args=(i,)) 
	thread.start() 

因此,我們對上面的代碼稍微的修改一下,就可以用相似的方法實現上面相似的功能:

# Python program creating 
# thread using multiprocessing 
# module 

import multiprocessing 
import time 

def func(number): 
	for i in range(1, 10): 
		time.sleep(0.01) 
		print('Processing ' + str(number) + ': prints ' + str(number*i)) 

for i in range(0, 3): 
	process = multiprocessing.Process(target=func, args=(i,)) 
	process.start() 

儘管這兩個模型的的接口函數非常的相似,但是它們的實現方法卻大相徑庭。所有的子線程共享全局變量,但是各個進程之間是相互獨立的。因此,殺死一個進程遠比殺死一個子線程要麻煩的多。Process類提供了一個terminate()方法去殺死一個子進程。

# Python program killing 
# a thread using multiprocessing 
# module 

import multiprocessing 
import time 

def func(number): 
	for i in range(1, 10): 
		time.sleep(0.01) 
		print('Processing ' + str(number) + ': prints ' + str(number*i)) 

# list of all processes, so that they can be killed afterwards 
all_processes = [] 

for i in range(0, 3): 
	process = multiprocessing.Process(target=func, args=(i,)) 
	process.start() 
	all_processes.append(process) 

# kill all processes after 0.03s 
time.sleep(0.03) 
for process in all_processes: 
	process.terminate() 

5. Killing Python thread by setting it as daemon

當主程序退出的時候,Daemon線程就會被殺死,比如:

import threading 
import time 
import sys 

def func(): 
	while True: 
		time.sleep(0.5) 
		print('Thread alive, and it won't die on program termination') 

t1 = threading.Thread(target=func) 
t1.start() 
time.sleep(2) 
sys.exit() 

需要注意的是,當主程序通過sys.exit()退出的時候,子線程t1仍然存活。在Python中,任何non-daemon的線程都會堵塞主線程,不讓主線程退出。但是daemon線程恰好相反,只要主線程退出,daemon線程就會被殺死。爲了聲明一個線程爲daemon線程,我們可以將daemon設置爲True。

# Python program killing 
# thread using daemon 

import threading 
import time 
import sys 

def func(): 
	while True: 
		time.sleep(0.5) 
		print('Thread alive, but it will die on program termination') 

t1 = threading.Thread(target=func) 
t1.daemon = True
t1.start() 
time.sleep(2) 
sys.exit() 

當然,我們可以使用面向對象的思維來實現殺死子線程的方法

import threading
import time
import sys


class Detector(threading.Thread):
    def __init__(self):
        super().__init__()

    def run(self):
        while True:
            time.sleep(0.5)
            print("這個是子線程")


if __name__ == '__main__':
    detector = Detector()
    detector.daemon = True
    detector.start()
    time.sleep(2)
    sys.exit()

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章