当前位置:Gxlcms > Python > python如何终止线程

python如何终止线程

时间:2021-07-01 10:21:17 帮助过:113人阅读

python终止线程的方法:1、调用stop函数,并使用join函数来等待线程合适地退出;2、在python线程里面raise一个Exception;3、用“thread.join”方式结束线程。

本文操作环境:windows7系统、python3.5版,DELL G3电脑。

前言 · 零

我们知道,在python里面要终止一个线程,常规的做法就是设置/检查 --->标志或者锁方式来实现的。

这种方式好不好呢?

应该是不大好的!因为在所有的程序语言里面,突然地终止一个线程,这无论如何都不是一个好的设计模式

同时

有些情况下更甚,比如:

  • 线程打开一个必须合理关闭的临界资源时,比如打开一个可读可写的文件;
  • 线程已经创建了好几个其他的线程,这些线程也是需要被关闭的(这可存在子孙线程游离的风险啊!)。

简单来说,就是我们一大群的线程共线了公共资源,你要其中一个线程“离场”,假如这个线程刚好占用着资源,那么强制让其离开的结局就是资源被锁死了,大家都拿不到了!怎么样是不是有点类似修仙类小说的情节!

知道为啥threading仅有start而没有end不?

你看,线程一般用在网络连接、释放系统资源、dump流文件,这些都跟IO相关了,你突然关闭线程那这些
没有合理地关闭怎么办?是不是就是给自己造bug呢?啊?!

因此这种事情中最重要的不是终止线程而是线程的清理啊。

解决方案 · 壹

一个比较nice的方式就是每个线程都带一个退出请求标志,在线程里面间隔一定的时间来检查一次,看是不是该自己离开了!

  1. import threading
  2. class StoppableThread(threading.Thread):
  3. """Thread class with a stop() method. The thread itself has to check
  4. regularly for the stopped() condition."""
  5. def __init__(self):
  6. super(StoppableThread, self).__init__()
  7. self._stop_event = threading.Event()
  8. def stop(self):
  9. self._stop_event.set()
  10. def stopped(self):
  11. return self._stop_event.is_set()

在这部分代码所示,当你想要退出线程的时候你应当显示调用stop()函数,并且使用join()函数来等待线程合适地退出。线程应当周期性地检测停止标志。

然而,还有一些使用场景中你真的需要kill掉一个线程:比如,当你封装了一个外部库,但是这个外部库在长时间调用,因此你想中断这个过程。

【推荐:python视频教程】

解决方案 · 貳

接下来的方案是允许在python线程里面raise一个Exception(当然是有一些限制的)。

  1. def _async_raise(tid, exctype):
  2. '''Raises an exception in the threads with id tid'''
  3. if not inspect.isclass(exctype):
  4. raise TypeError("Only types can be raised (not instances)")
  5. res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid,
  6. ctypes.py_object(exctype))
  7. if res == 0:
  8. raise ValueError("invalid thread id")
  9. elif res != 1:
  10. # "if it returns a number greater than one, you're in trouble,
  11. # and you should call it again with exc=NULL to revert the effect"
  12. ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0)
  13. raise SystemError("PyThreadState_SetAsyncExc failed")
  14. class ThreadWithExc(threading.Thread):
  15. '''A thread class that supports raising exception in the thread from
  16. another thread.
  17. '''
  18. def _get_my_tid(self):
  19. """determines this (self's) thread id
  20. CAREFUL : this function is executed in the context of the caller
  21. thread, to get the identity of the thread represented by this
  22. instance.
  23. """
  24. if not self.isAlive():
  25. raise threading.ThreadError("the thread is not active")
  26. # do we have it cached?
  27. if hasattr(self, "_thread_id"):
  28. return self._thread_id
  29. # no, look for it in the _active dict
  30. for tid, tobj in threading._active.items():
  31. if tobj is self:
  32. self._thread_id = tid
  33. return tid
  34. # TODO: in python 2.6, there's a simpler way to do : self.ident
  35. raise AssertionError("could not determine the thread's id")
  36. def raiseExc(self, exctype):
  37. """Raises the given exception type in the context of this thread.
  38. If the thread is busy in a system call (time.sleep(),
  39. socket.accept(), ...), the exception is simply ignored.
  40. If you are sure that your exception should terminate the thread,
  41. one way to ensure that it works is:
  42. t = ThreadWithExc( ... )
  43. ...
  44. t.raiseExc( SomeException )
  45. while t.isAlive():
  46. time.sleep( 0.1 )
  47. t.raiseExc( SomeException )
  48. If the exception is to be caught by the thread, you need a way to
  49. check that your thread has caught it.
  50. CAREFUL : this function is executed in the context of the
  51. caller thread, to raise an excpetion in the context of the
  52. thread represented by this instance.
  53. """
  54. _async_raise( self._get_my_tid(), exctype )

正如注释里面描述,这不是啥“灵丹妙药”,因为,假如线程在python解释器之外busy,这样子的话终端异常就抓不到啦~

这个代码的合理使用方式是:让线程抓住一个特定的异常然后执行清理操作。这样的话你就能终端一个任务并能合适地进行清除。

解决方案 · 叁

假如我们要做个啥事情,类似于中断的方式,那么我们就可以用thread.join方式。

  1. join的原理就是依次检验线程池中的线程是否结束,没有结束就阻塞直到线程结束,如果结束则跳转执行下一个线程的join函数。
  2. 先看看这个:
  3. 1. 阻塞主进程,专注于执行多线程中的程序。
  4. 2. 多线程多join的情况下,依次执行各线程的join方法,前头一个结束了才能执行后面一个。
  5. 3. 无参数,则等待到该线程结束,才开始执行下一个线程的join。
  6. 4. 参数timeout为线程的阻塞时间,如 timeout=2 就是罩着这个线程2s 以后,就不管他了,继续执行下面的代码。
  1. # coding: utf-8
  2. # 多线程join
  3. import threading, time
  4. def doWaiting1():
  5. print 'start waiting1: ' + time.strftime('%H:%M:%S') + "\n"
  6. time.sleep(3)
  7. print 'stop waiting1: ' + time.strftime('%H:%M:%S') + "\n"
  8. def doWaiting2():
  9. print 'start waiting2: ' + time.strftime('%H:%M:%S') + "\n"
  10. time.sleep(8)
  11. print 'stop waiting2: ', time.strftime('%H:%M:%S') + "\n"
  12. tsk = []
  13. thread1 = threading.Thread(target = doWaiting1)
  14. thread1.start()
  15. tsk.append(thread1)
  16. thread2 = threading.Thread(target = doWaiting2)
  17. thread2.start()
  18. tsk.append(thread2)
  19. print 'start join: ' + time.strftime('%H:%M:%S') + "\n"
  20. for tt in tsk:
  21. tt.join()
  22. print 'end join: ' + time.strftime('%H:%M:%S') + "\n"

默认join方式,也就是不带参,阻塞模式,只有子线程运行完才运行其他的。

1、 两个线程在同一时间开启,join 函数执行。

2、waiting1 线程执行(等待)了3s 以后,结束。

3、waiting2 线程执行(等待)了8s 以后,运行结束。

4、join 函数(返回到了主进程)执行结束。

这里是默认的join方式,是在线程已经开始跑了之后,然后再join的,注意这点,join之后主线程就必须等子线程结束才会返回主线。

join的参数,也就是timeout参数,改为2,即join(2),那么结果就是如下了:

  1. 两个线程在同一时间开启,join 函数执行。

  2. wating1 线程在执行(等待)了三秒以后,完成。

  3. join 退出(两个2s,一共4s,36-32=4,无误)。

  4. waiting2 线程由于没有在 join 规定的等待时间内(4s)完成,所以自己在后面执行完成。

join(2)就是:我给你子线程两秒钟,每个的2s钟结束之后我就走,我不会有丝毫的顾虑!

以上就是python如何终止线程的详细内容,更多请关注gxlcms其它相关文章!

人气教程排行