[Python 多线程] 详解daemon属性值None,False,True的区别 (五)

时间:2022-10-25 22:18:24

本文以多个例子介绍Python多线程中daemon属性值的区别。

回顾:

前面的文章简单介绍了在现代操作系统中,每一个进程都认为自己独占所有的计算机资源。

或者说线程就是独立的王国,进程间是相对独立的,不可以随便的共享数据。

线程就是省份,同一个进程内的线程可以共享进程的资源,每一个线程拥有自己的堆栈。

  • 每个进程至少要有一个线程,并最为程序的入口,这个进程就是主线程。
  • 每个进程至少要有一个主线程,其它线程称为工作线程。
  • 父线程:如果线程A启动了一个线程B,A就是B的父线程。
  • 子线程:B就是A的子线程

Python中,在构造线程对象时,可以设置daemon属性,这个属性必须在start方法前设置好。

主线程是程序启动的第一个线程,主线程可以再启动 n 个子线程。

daemon属性可以不设置,默认为None,主线程默认是False。

看一段daemon属性在源码中是如何设计的:

class Thread:
def __init__(self, group=None, target=None, name=None,
args=(), kwargs=None, *, daemon=None):#daemon属性值默认是None
if daemon is not None:
self._daemonic = daemon
else:
self._daemonic = current_thread().daemon

  在看完下面的几个例子之后,就会理解源码中的意思了。

daemon属性值分为以下三种:

1) daemon=False

当daemon为False时,父线程在运行完毕后,会等待所有子线程退出才结束程序。

举例:

import threading
import time def foo():
for i in range(3):
print('i={},foo thread daemon is {}'.format(i,threading.current_thread().isDaemon()))
time.sleep(1) t = threading.Thread(target=foo,daemon=False)
t.start() print("Main thread daemon is {}".format(threading.current_thread().isDaemon()))
print("Main Thread Exit.") 运行结果:
i=0,foo thread daemon is False
Main thread daemon is False
Main Thread Exit.
i=1,foo thread daemon is False
i=2,foo thread daemon is False

  通过 isDaemon() 方法可以返回当前线程的daemon值,主线程默认是False,子线程也是False的原因是创建线程对象时指定了daemon=False。

  根据运行结果的顺序可以得知,主程序在线程完线程对象后就立即启动了,然后子线程返回了结果中第一行内容,然后sleep 1秒模拟 IO,这时CPU发现子线程阻塞了,就立即切到主线程继续执行,主线程先后打印第二行和第三行,此时主线程的代码已经执行到结尾。然后,因为主线程为子线程设置了daemon=False属性,这时就又发生了 线程切换到子线程,子线程先后执行完第四行和第五行,然后子线程就完全执行完毕,主线程看到子线程退出以后,也立即退出,整个程序结束。

  

2) daemon=True

当daemon为True时,父线程在运行完毕后,子线程无论是否正在运行,都会伴随主线程一起退出。

举例:

import threading
import time def foo():
for i in range(3):
print('i={},foo thread daemon is {}'.format(i,threading.current_thread().isDaemon()))
time.sleep(1) t = threading.Thread(target=foo,daemon=True)
t.start() print("Main thread daemon is {}".format(threading.current_thread().isDaemon()))
print("Main Thread Exit.") 运行结果 :
i=0,foo thread daemon is True
Main thread daemon is False
Main Thread Exit.

  从运行结果来看,当子线程设置daemon属性为True时,即主线程不关心子线程运行状态,主线程退出,子线程也必须跟着退出。

  所以运行结果中子线程只执行了一句语句,就轮到主线程,主线程执行完最后两句,就立即退出,整个程序结束。

3) 不设置,或者daemon=None

daemon属性可以不设置,默认值是None。

举例:

import threading
import time def bar():
while True: # 无限循环的子子线程
print('【bar】 daemon is {}'.format(threading.current_thread().isDaemon()))
time.sleep(1) def foo():
for i in range(3): #启动3个子线程
print('i={},【foo】 thread daemon is {}'.format(i,threading.current_thread().isDaemon()))
t1 = threading.Thread(target=bar,daemon=None)
t1.start() t = threading.Thread(target=foo,daemon=True)
t.start() print("Main thread daemon is {}".format(threading.current_thread().isDaemon()))
time.sleep(2)
print("Main Thread Exit.") 运行结果:
i=0,【foo】 thread daemon is True
Main thread daemon is False
【bar】 daemon is True
i=1,【foo】 thread daemon is True
【bar】 daemon is True
i=2,【foo】 thread daemon is True
【bar】 daemon is True
【bar】 daemon is True
【bar】 daemon is True
【bar】 daemon is True
Main Thread Exit.

  这里在主线程中使用了延迟2秒,来让子线程启动的子线程有机会输出其daemon属性值,如果不设置延迟,因为子线程设置了daemon=Ture,子子线程daemon为None就相当于取的是父线程的daemon值(子子线程的父线程也就是子线程,子线程daemon=true),所以最终子子线程中的while无限循环还是被它的父线程(子线程)强制退出了。

再分别看下子子线程的daemon为False的情况:

import threading
import time def bar():
while True: # 无限循环的子子线程
print('【bar】 daemon is {}'.format(threading.current_thread().isDaemon()))
time.sleep(1) def foo():
for i in range(3): #启动3个子线程
print('i={},【foo】 thread daemon is {}'.format(i,threading.current_thread().isDaemon()))
t1 = threading.Thread(target=bar,daemon=False)
t1.start() t = threading.Thread(target=foo,daemon=True)
t.start() print("Main thread daemon is {}".format(threading.current_thread().isDaemon()))
time.sleep(2)
print("Main Thread Exit.") 运行结果:
i=0,【foo】 thread daemon is True
Main thread daemon is False
【bar】 daemon is False
i=1,【foo】 thread daemon is True
【bar】 daemon is False
i=2,【foo】 thread daemon is True
【bar】 daemon is False
【bar】 daemon is False
【bar】 daemon is False
【bar】 daemon is False
Main Thread Exit.
【bar】 daemon is False
【bar】 daemon is False
【bar】 daemon is False
【bar】 daemon is False
【bar】 daemon is False
【bar】 daemon is False
【bar】 daemon is False
【bar】 daemon is False
【bar】 daemon is False
【bar】 daemon is False
【bar】 daemon is False
.......无限循环....

  主线程本来是不等子线程执行完毕的,但子线程要等待子子线程执行完毕,子子线程又是无限循环。所以最终主线程也拦不住子子线程一直疯狂的输出,这就好比爷爷管得了儿子,但管不了孙子呀。

上面这个例子最后第二行的sleep(2)是在主线程中运行的,如果注释掉这条语句,就会发现运行结果是这样的:

i=0,【foo】 thread daemon is True
Main thread daemon is False
Main Thread Exit.

  子线程虽然运行了,但还没来得及启动子子线程,主线程就执行到最后了,直接结束掉了程序。

  如果那怕让子子线程启动起来一个,就主线程就没辙了,这家伙疯狂的输出。

  所以如果没有sleep(2)这条语句,就看不到真正的效果了。

总结:

现在再来看daemon属性在Thread类中的源码,就可以理解了。

class Thread:
def __init__(self, group=None, target=None, name=None,
args=(), kwargs=None, *, daemon=None):#daemon属性值默认是None
if daemon is not None:
self._daemonic = daemon
else:
self._daemonic = current_thread().daemon

  大致逻辑如下:

创建线程对象时传入daemon属性值

如果值不是None,也就是说传入的是True或者False,当然这是假设,万一有变态传入乱七八糟的值呢,不过解释器在运行时肯定会抛异常。

传入的值是就作为该目标线程的daemon值。

如果没有传入值,或传入的是daemon=None,就等同于None,该目标线程的值就取父线程的daemon值作为自己的daemon的值。

也就是要分清楚源码中的current_thread()是哪个线程,在第三个例子中,是在子线程中创建子子线程对象,所以current_thread()这个当前线程就是子线程,子子线程没有传入daemon属性值,所以创建时就把子线程的daemon属性值作为该子子线程的daemon属性值。

考虑这样的场景,主线程只启动了子线程,子线程么有子子线程,子线程没有设置daemon属性时,那就是谁创建这个线程(当然是主线程创建的),就把它的daemon属性值作为这个线程的daemon值。主线程默认的daemon值是False,所以这个子线程最终也会传入的是False。

  所以:

import threading
import time def foo():
for i in range(3):
print('i={},【foo】 thread daemon is {}'.format(i,threading.current_thread().isDaemon()))
time.sleep(1) t = threading.Thread(target=foo)
t.start() print("Main thread daemon is {}".format(threading.current_thread().isDaemon()))
# time.sleep(2)
print("Main Thread Exit.") 运行结果:
i=0,【foo】 thread daemon is False
Main thread daemon is False
Main Thread Exit.
i=1,【foo】 thread daemon is False
i=2,【foo】 thread daemon is False

  子线程 daemon=False。

.

[Python 多线程] 详解daemon属性值None,False,True的区别 (五)的更多相关文章

  1. python多线程详解

    目录 python多线程详解 一.线程介绍 什么是线程 为什么要使用多线程 二.线程实现 threading模块 自定义线程 守护线程 主线程等待子线程结束 多线程共享全局变量 互斥锁 递归锁 信号量 ...

  2. 说说Python多线程中的daemon属性方法

    大家看多线程部分的时候肯定看到过daemon这个属性,当我在百度了一圈后也没发现有比较好的解释(或者大家对这个解释都非常清楚),于是自己通过代码和官方介绍了解它,进行了一些总结 给大家一些参考. 首先 ...

  3. 第7.24节 Python案例详解:使用property函数定义属性简化属性访问代码实现

    第7.24节 Python案例详解:使用property函数定义属性简化属性访问代码实现 一.    案例说明 本节将通过一个案例介绍怎么使用property定义快捷的属性访问.案例中使用Rectan ...

  4. 第7.27节 Python案例详解: @property装饰器定义属性访问方法getter、setter、deleter

    上节详细介绍了利用@property装饰器定义属性的语法,本节通过具体案例来进一步说明. 一.    案例说明 本节的案例是定义Rectangle(长方形)类,为了说明问题,除构造函数外,其他方法都只 ...

  5. 第7.25节 Python案例详解:使用property函数定义与实例变量同名的属性会怎样?

    第7.25节 Python案例详解:使用property函数定义与实例变量同名的属性会怎样? 一.    案例说明 我们上节提到了,使用property函数定义的属性不要与类内已经定义的普通实例变量重 ...

  6. [转] Python Traceback详解

    追莫名其妙的bugs利器-mark- 转自:https://www.jianshu.com/p/a8cb5375171a   Python Traceback详解   刚接触Python的时候,简单的 ...

  7. python 数据类型详解

    python数据类型详解 参考网址:http://www.cnblogs.com/linjiqin/p/3608541.html 目录1.字符串2.布尔类型3.整数4.浮点数5.数字6.列表7.元组8 ...

  8. python线程详解

    #线程状态 #线程同步(锁)#多线程的优势在于可以同时运行多个任务,至少感觉起来是这样,但是当线程需要共享数据时,可能存在数据不同步的问题. #threading模块#常用方法:'''threadin ...

  9. python数据类型详解(全面)

    python数据类型详解 目录1.字符串2.布尔类型3.整数4.浮点数5.数字6.列表7.元组8.字典9.日期 1.字符串1.1.如何在Python中使用字符串a.使用单引号(')用单引号括起来表示字 ...

随机推荐

  1. webService

    什么是webService WebService,顾名思义就是基于Web的服务.它使用Web(HTTP)方式,接收和响应外部系统的某种请求.从而实现远程调用.  1:从WebService的工作模式上 ...

  2. HDFS 异常处理与恢复

    在前面的文章 <HDFS DataNode 设计实现解析>中我们对文件操作进行了描述,但并未展开讲述其中涉及的异常错误处理与恢复机制.本文将深入探讨 HDFS 文件操作涉及的错误处理与恢复 ...

  3. 【nginx运维基础&lpar;7&rpar;】常用PHP开源程序的NginxRewrite示例

    在写伪静态的时候,可以先用一个打印$_GET的PHP文件来测试,并且一定注意浏览器缓存,另外正则里如果有"{}",正则要用双引号包起来 dedecms location / { r ...

  4. 一种H&period;264高清视频的无参考视频质量评价算法(基于QP和跳过宏块数)

    本文记录一种无参考视频质量评价算法.这是我们自己实验室前两年一个师姐做的,算法还是比较准确的,在此记录一下. 注意本算法前提是高清视频.而且是H.264编码方式. 该方法主要使用两个码流里面的参数进行 ...

  5. FCC中级算法&lpar;上&rpar;

    在学习FCC中级算法这一块,自己遇到了很多问题,通过RSA也慢慢把问题解决了,发现每一个问题都会有很多的解决思路,因此把自己想到的一些思路记录到这里. 1. Sum All Numbers in a ...

  6. 《http权威指南》读书笔记13

    概述 最近对http很感兴趣,于是开始看<http权威指南>.别人都说这本书有点老了,而且内容太多.我个人觉得这本书写的太好了,非常长知识,让你知道关于http的很多概念,不仅告诉你怎么做 ...

  7. HttpRunner Manager接口自动化测试平台实践(Windows)

    1. 源码下载 github: https://github.com/HttpRunner/HttpRunnerManager 下载后放入项目目录,结构如下: 2.依赖环境  根据根目录require ...

  8. yum仓库中源的配置与使用

    yum 主要功能是更方便的添加/删除/更新RPM 包,自动解决包的倚赖性问题,便于管理大量系统的更新问题. yum 可以同时配置多个资源库(Repository),简洁的配置文件(/etc/yum.c ...

  9. java解决查找问题

    1.给定一个字符串,找到里面的大写字母和小写字母以及其他字母的个数: 代码: package test; public class Stringclass { public static void m ...

  10. windows 环境下如何使用virtualenv python环境管理工具

    1.安装工具 pip install virtualenv 2.新建项目文件夹并在文件夹内安装环境并命名为my_env,加入 virtualenv C:\Users\yxx\wp\my_env 3.激 ...