Python|杀死线程的不同方法
通常,突然终止线程被认为是一种不好的编程习惯。突然终止线程可能会使必须正确关闭的关键资源处于打开状态。但是一旦某个特定的时间段过去或产生了一些中断,您可能想终止一个线程。有多种方法可以杀死Python中的线程。
- 在Python线程中引发异常
- 设置/重置停止标志
- 使用跟踪杀死线程
- 使用多处理模块杀死线程
- 通过将其设置为守护进程来杀死Python线程
- 使用隐藏函数_stop()
在Python线程中引发异常:
此方法使用函数PyThreadState_SetAsyncExc() 在线程中引发异常。例如,
Python3
# 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()
Python3
# 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')
Python3
# 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()
Python3
# 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')
Python3
# 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()
Python3
# 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()
Python3
# 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()
Python3
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()
Python3
# 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()
Python3
# Python program killing
# a thread using ._stop()
# function
import time
import threading
class MyThread(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(MyThread, self).__init__(*args, **kwargs)
self._stop = threading.Event()
# function using _stop function
def stop(self):
self._stop.set()
def stopped(self):
return self._stop.isSet()
def run(self):
while True:
if self.stopped():
return
print("Hello, world!")
time.sleep(1)
t1 = MyThread()
t1.start()
time.sleep(5)
t1.stop()
t1.join()
当我们在机器上运行上面的代码时,你会注意到,只要函数raise_exception() 被调用,目标函数run() 就会结束。这是因为一旦引发异常,程序控制就会跳出 try 块并终止 run()函数。之后可以调用 join()函数来终止线程。在没有函数run_exception() 的情况下,目标函数run() 将永远运行,并且永远不会调用 join()函数来终止线程。设置/重置停止标志:
为了杀死一个线程,我们可以声明一个停止标志,这个标志会被线程偶尔检查。例如
Python3
# 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() 来杀死。但是由于某些原因,人们可能会避免使用全局变量。对于这些情况,可以传递函数对象以提供类似的功能,如下所示。
Python3
# 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()函数就结束并且线程可以被杀死。使用跟踪杀死线程:
此方法通过在每个线程中安装跟踪来工作。每个跟踪都会在检测到某些刺激或标志时自行终止,从而立即终止相关线程。例如
Python3
# 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')
在此代码中,start() 稍作修改,以使用 settrace() 设置系统跟踪函数。本地跟踪函数被定义为,只要设置了相应线程的终止标志(已终止),就会在执行下一行代码时引发 SystemExit 异常,从而结束目标函数func 的执行。现在可以使用 join() 终止线程。使用多处理模块杀死线程:
Python的 multiprocessing 模块允许您以与使用 threading 模块产生线程类似的方式产生进程。多线程模块的接口与线程模块的接口类似。例如,在给定的代码中,我们创建了三个从 1 到 9 的线程(进程)。
Python3
# 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()
上述代码的功能也可以通过类似的方式使用多处理模块来实现,只需很少的改动。请参阅下面给出的代码。
Python3
# 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()。现在,回到最初的问题。假设在上面的代码中,我们想在 0.03s 过去后杀死所有进程。此功能是使用以下代码中的多处理模块实现的。
Python3
# 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()
虽然这两个模块有不同的实现。上面代码中的多处理模块提供的这个功能类似于杀死线程。因此,当我们需要在Python中实现线程终止时,多处理模块可以用作一个简单的替代方案。通过将Python线程设置为 daemon 来杀死它:
守护线程是那些在主程序退出时被杀死的线程。例如
Python3
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()
请注意,线程 t1 保持活动状态并阻止主程序通过 sys.exit() 退出。在Python中,任何活着的非守护线程都会阻塞主程序退出。然而,一旦主程序退出,守护线程本身就会被杀死。换句话说,一旦主程序退出,所有的守护线程都会被杀死。要将线程声明为守护进程,我们将关键字参数 daemon 设置为 True。例如,在给定的代码中,它演示了守护线程的属性。
Python3
# 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()
请注意,一旦主程序退出,线程 t1 就会被杀死。在可以使用程序终止来触发线程终止的情况下,这种方法被证明是非常有用的。请注意,在Python中,只要所有非守护线程都死了,主程序就会终止,而与活着的守护线程的数量无关。因此,这些守护线程所持有的资源,例如打开的文件、数据库事务等,可能无法正常释放。 Python程序中的初始控制线程不是守护线程。不建议强行杀死线程,除非确定这样做不会导致任何泄漏或死锁。
使用隐藏函数_stop() :
为了杀死一个线程,我们使用了隐藏函数_stop() 这个函数没有记录,但可能会在下一个版本的Python中消失。
Python3
# Python program killing
# a thread using ._stop()
# function
import time
import threading
class MyThread(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(MyThread, self).__init__(*args, **kwargs)
self._stop = threading.Event()
# function using _stop function
def stop(self):
self._stop.set()
def stopped(self):
return self._stop.isSet()
def run(self):
while True:
if self.stopped():
return
print("Hello, world!")
time.sleep(1)
t1 = MyThread()
t1.start()
time.sleep(5)
t1.stop()
t1.join()
注意:上述方法在某些情况下可能不起作用,因为Python没有提供任何直接杀死线程的方法。