python threading setdaemon_Python线程为什么搞个setDaemon

python threading setdaemon_Python线程为什么搞个setDaemon渣渣飞渣渣飞 长年在票圈深夜放毒 是网易游戏高级运维工程师 对代码性能及系统原理饶有兴趣 三人行 必有我师 现负责监控相关业务开发 前言使用 Python 都不会错过线程这个知识 但是每次谈到线程 大家都下意识说 GIL 全局锁 但其实除了这个老生常谈的话题 还有很多有价值的东西可以探索的 譬如 setDaemon 线程的使用与存在的问题我们会写这样的代码来启动多线程 1importti

python threading setdaemon_Python线程为什么搞个setDaemon渣渣飞

渣渣飞,长年在票圈深夜放毒,是网易游戏高级运维工程师,对代码性能及系统原理饶有兴趣,三人行,必有我师。现负责监控相关业务开发。

前言

使用 Python 都不会错过线程这个知识,但是每次谈到线程,大家都下意识说 GIL 全局锁,

但其实除了这个老生常谈的话题,还有很多有价值的东西可以探索的,譬如:setDaemon()。

线程的使用 与 存在的问题

我们会写这样的代码来启动多线程:

1import time

2import threading

3

4def test():

5 while True:

6 print threading.currentThread()

7 time.sleep(1)

8

9if __name__ == ‘__main__’:

10 t1 = threading.Thread(target=test)

11 t2 = threading.Thread(target=test)

12 t1.start()

13 t2.start()

输出:

1^C

2

3^C^C^C^C^C^C # ctrl-c 多次都无法中断

4

5^C

6

7

8

9

10…(两个线程竞相打印)

通过 Threading 我们可以很简单的实现并发的需求,但是同时也给我们带来了一个大难题: 怎么退出呢?

在上面的程序运行中,我已经尝试按了多次的 ctrl-c,都无法中断这程序工作的热情!最后是迫不得已用 kill 才结束。

那么怎样才能可以避免这种问题呢?或者说,怎样才能在主线程退出的时候,子线程也自动退出呢?

守护线程

有过相似经验的老司机肯定就知道,setDaemon() 将线程搞成 守护线程 不就得了呗:

1import time

2import threading

3

4def test():

5 while True:

6 print threading.currentThread()

7 time.sleep(1)

8

9if __name__ == ‘__main__’:

10 t1 = threading.Thread(target=test)

11 t1.setDaemon(True)

12 t1.start()

13

14 t2 = threading.Thread(target=test)

15 t2.setDaemon(True)

16 t2.start()

输出:

1python2.7 1.py

2

3

4(直接退出了)

直接退出?理所当然,因为主线程已经执行完了,确实是已经结束了,正因为设置了守护线程,所以这时候子线程也一并退出了。

突如其来的 daemon

那么问题来了,我们以前学 C 语言的时候,好像不用 Daemon 也可以啊,比如这个:

1#include

2#include

3#include

4

5void *test(void *args)

6{

7 while (1)

8 {

9 printf(“ThreadID: %d\n”, syscall(SYS_gettid));

10 sleep(1);

11 }

12}

13

14int main()

15{

16 pthread_t t1 ;

17 int ret = pthread_create(&t1, NULL, test, NULL);

18 if (ret != 0)

19 {

20 printf(“Thread create failed\n”);

21 }

22

23 // 避免直接退出

24 sleep(2);

25 printf(“Main run..\n”);

26}

输出:

1# gcc -lpthread test_pytha.out & ./a

2ThreadID: 31233

3ThreadID: 31233

4Main run.. (毫不犹豫退出了)

既然 Python 也是用 C 写的,为什么 Python 多线程退出需要 setDaemon ???

想要解决这个问题,我们怕不是要从主线程退出的一刻开始讲起,从前….

反藤摸瓜

Python 解析器在结束的时候,会调用 wait_for_thread_shutdown 来做个例行清理:

1// python2.7/python/pythonrun.c

2

3static void

4wait_for_thread_shutdown(void)

5{

6#ifdef WITH_THREAD

7 PyObject *result;

8 PyThreadState *tstate = PyThreadState_GET();

9 PyObject *threading = PyMapping_GetItemString(tstate->interp->modules,

10 “threading”);

11 if (threading == NULL) {

12 /* threading not imported */

13 PyErr_Clear();

14 return;

15 }

16 result = PyObject_CallMethod(threading, “_shutdown”, “”);

17 if (result == NULL)

18 PyErr_WriteUnraisable(threading);

19 else

20 Py_DECREF(result);

21 Py_DECREF(threading);

22#endif

23}

我们看到 #ifdef WITH_THREAD 就大概猜到对于是否多线程,这个函数是运行了不同的逻辑的

很明显,我们上面的脚本,就是命中了这个线程逻辑,所以它会动态 import threading 模块,然后执行 _shutdown 函数。

这个函数的内容,我们可以从 threading 模块看到:

1# /usr/lib/python2.7/threading.py

2

3_shutdown = _MainThread()._exitfunc

4

5class _MainThread(Thread):

6

7 def __init__(self):

8 Thread.__init__(self, name=”MainThread”)

9 self._Thread__started.set()

10 self._set_ident()

11 with _active_limbo_lock:

12 _active[_get_ident()] = self

13

14 def _set_daemon(self):

15 return False

16

17 def _exitfunc(self):

18 self._Thread__stop()

19 t = _pickSomeNonDaemonThread()

20 if t:

21 if __debug__:

22 self._note(“%s: waiting for other threads”, self)

23 while t:

24 t.join()

25 t = _pickSomeNonDaemonThread()

26 if __debug__:

27 self._note(“%s: exiting”, self)

28 self._Thread__delete()

29

30def _pickSomeNonDaemonThread():

31 for t in enumerate():

32 if not t.daemon and t.is_alive():

33 return t

34 return None

_shutdown 实际上也就是 _MainThread()._exitfunc 的内容,主要是将 enumerate() 返回的所有结果,全部 join() 回收

而 enumerate() 是什么?

这个平时我们也会使用,就是当前进程的所有 符合条件 的 Python线程对象:

1>>> print threading.enumerate()

2[<_mainthread started>]

1# /usr/lib/python2.7/threading.py

2

3def enumerate():

4 “””Return a list of all Thread objects currently alive.

5

6 The list includes daemonic threads, dummy thread objects created by

7 current_thread(), and the main thread. It excludes terminated threads and

8 threads that have not yet been started.

9

10 “””

11 with _active_limbo_lock:

12 return _active.values() + _limbo.values()

符合条件???符合什么条件??不着急,容我娓娓道来:

从起源谈存活条件

在 Python 的线程模型里面,虽然有 GIL 的干涉,但是线程却是实实在在的原生线程

Python 只是多加一层封装: t_bootstrap,然后再在这层封装里面执行真正的处理函数。

在 threading 模块内,我们也能看到一个相似的:

1# /usr/lib/python2.7/threading.py

2

3class Thread(_Verbose):

4 def start(self):

5 …省略

6 with _active_limbo_lock:

7 _limbo[self] = self # 重点

8 try:

9 _start_new_thread(self.__bootstrap, ())

10 except Exception:

11 with _active_limbo_lock:

12 del _limbo[self] # 重点

13 raise

14 self.__started.wait()

15

16 def __bootstrap(self):

17 try:

18 self.__bootstrap_inner()

19 except:

20 if self.__daemonic and _sys is None:

21 return

22 raise

23

24 def __bootstrap_inner(self):

25 try:

26 …省略

27 with _active_limbo_lock:

28 _active[self.__ident] = self # 重点

29 del _limbo[self] # 重点

30 …省略

在上面的一连串代码中,_limbo 和 _active 的变化都已经标记了重点,我们可以得到下面的定义:

1 _limbo : 就是调用了 start,但是还没来得及 _start_new_thread 的对象

2 _active: 活生生的线程对象

那么回到上文,当 _MainThread()._exitfunc 执行时,是会检查整个进程是否存在 _limbo + _active 的对象,

只要存在一个,就会调用 join(), 这个也就是堵塞的原因。

setDaemon 用处

无限期堵塞不行,自作聪明帮用户强杀线程也不是办法,那么怎么做才会比较优雅呢?

那就是提供一个途径,让用户来设置随进程退出的标记,那就是 setDaemon:

1class Thread():

2 …省略

3 def setDaemon(self, daemonic):

4 self.daemon = daemonic

5

6 …省略

7

8# 其实上面也贴了,这里再贴一次

9def _pickSomeNonDaemonThread():

10 for t in enumerate():

11 if not t.daemon and t.is_alive():

12 return t

13 return None

只要子线程,全部设置 setDaemon(True), 那么主线程一准备退出,全都乖乖地由操作系统销毁回收。

之前一直很好奇,pthread 都没有 daemon 属性,为什么 Python 会有呢?

结果这玩意就是真的是仅作用于 Python 层(手动笑脸)

结语

区区一个 setDaemon 可以引出很多本质内容的探索机会,比如线程的创建过程,管理流程等。

这些都是很有意思的内容,我们应该大胆探索,不局限于使用~

python threading setdaemon_Python线程为什么搞个setDaemon

往期精彩

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/229019.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月16日 下午5:39
下一篇 2026年3月16日 下午5:39


相关推荐

  • C# dll反编译(dll反编译破解)

    下面是修改IL文件然后重新生成DLL的步骤,适用于dll文件没有源码,但是需要修改一点点的小改动问:为啥不直接用dnspy进行修改?答:我这边需要修改.net2.0上编译的一个库,用dnspy反编译后修改了代码,,运行反倒有问题,因此怀疑通过dnspy修改库最后的编译环境和原有库的编译环境不一致,考虑到dnspy自身需要.net4.0以上环境才能运行,所以这里采用修改IL文件的方法…

    2022年4月12日
    1.2K
  • 时序数据 mysql存储_【时序数据库】时序数据库介绍

    时序数据 mysql存储_【时序数据库】时序数据库介绍1.基本概念时序数据库(TimeSeriesDatabase)是用于存储和管理时间序列数据的专业化数据库。时序数据库特别适用于物联网设备监控和互联网业务监控场景。下面介绍下时序数据库的一些基本概念(不同的时序数据库称呼略有不同)。1.1度量(metric)监测数据的指标,例如风力和温度。相当于关系型数据库中的table。1.2标签(tag)指标项监测针对的具体对象,属于指定度量下的数据子类…

    2026年4月17日
    2
  • oracle创建表的sql语句「建议收藏」

    oracle创建表的sql语句「建议收藏」1.oracle创建表sql语句

    2022年10月16日
    4
  • 腾讯C端市场加码元宝,巨头纷纷“砸钱”,AI流量入口争夺战升级

    腾讯C端市场加码元宝,巨头纷纷“砸钱”,AI流量入口争夺战升级

    2026年3月13日
    3
  • EVE模拟器的安装使用

    EVE模拟器的安装使用文章目录模拟器的安装一、为什么要使用模拟器?二、模拟环境要求1、硬件配置要求:2、系统配置要求三、模拟器的配置流程1、安装vmwareworkstation虚拟软件2、下载eve工具3、安装搜狗浏览器4、安装EVE工具包5、打开EVE虚拟机6、测试eve是否能正常使用模拟器的使用模拟器的安装一、为什么要使用模拟器?以前学习网络,没有一个好的模拟器,大家都用真实的设备做实验,但是效率太低了,而且每次学习新的内容需要自己插拔网线,搭建新的拓扑(或者使用万能拓扑),这样学习的效率十分低下,而且十分困难,导致

    2022年6月12日
    36
  • Pytest(1)安装与入门[通俗易懂]

    Pytest(1)安装与入门[通俗易懂]pytest介绍pytest是python的一种单元测试框架,与python自带的unittest测试框架类似,但是比unittest框架使用起来更简洁,效率更高。根据pytest的官方网站介绍,它

    2022年7月31日
    6

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号