📜  Python|杀死线程的不同方法

📅  最后修改于: 2022-05-13 01:55:17.696000             🧑  作者: Mango

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没有提供任何直接杀死线程的方法。