想看英文的可以點擊這裏
通常來說,突然殺死一個子線程往往不是一個好的選擇。但是,在某些特定的場合,我們需要殺死子線程。下面介紹了五種不同的方法殺死子線程:
- 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()