Python 多线程 线程安全、daemon简介 (四)
线程安全
只能在Winodws下的ipython中演示,Python命令行、Pycharm、Mac下的ipython都演示不出效果
import threading def worker():
for _ in range(100):
print("{} is running.".format(threading.current_thread().name)) for i in range(1,5):
name = "worker-{}".format(i)
t = threading.Thread(target=worker,name=name)
t.start()
这里只展示在Windows下ipython中的运行结果:
对比代码,输出应该是一行一行打印,但是在ipython中有个别行打印时连在了一起。
print属于线程内的语句,且print函数动作分两部分,打印字符串和打印换行,该线程print拼接换行之前被其它线程打断了,这说明print函数是线程不安全的。
简单理解,线程不安全就是正在做某事,被打断了。
线程安全的解决方法:
1、拼接字符串
print("{} is running\n".format(threading.current_thread()),end="")
在print打印字符串时就打印'\n'换行符,因为字符串是不可变类型,它可以作为一个整体不被分割输出。
2、logging模块
logging模块是分级别输出,不允许被打断,是安全的。
import threading
import time
import logging
logging.basicConfig(level=logging.INFO) #警告级别 def worker():
for x in range(100):
# time.sleep(0.5)
msg = ("{} is running".format(threading.current_thread()))
logging.info(msg) for i in range(1):
t = threading.Thread(target=worker,name='worker-{}'.format(i))
t.start() 运行结果:
INFO:root:<Thread(worker-0, started 123145503371264)> is running
INFO:root:<Thread(worker-0, started 123145503371264)> is running
INFO:root:<Thread(worker-0, started 123145503371264)> is running
INFO:root:<Thread(worker-0, started 123145503371264)> is running
INFO:root:<Thread(worker-0, started 123145503371264)> is running
INFO:root:<Thread(worker-0, started 123145503371264)> is running
INFO:root:<Thread(worker-0, started 123145503371264)> is running
......
关于logging模块下一篇详细介绍。
daemon线程与non-daemon线程:
进程靠线程执行代码,一个进程至少有一个主线程,其它线程都是工作线程。
主线程是第一个启动的线程。
父线程:如果线程A启动了一个线程B,A就是B的父线程。
子线程:B就是A的子线程。
daemon属性要在start()方法之前设置。
daemon=None 或者不设置时, 等同于父线程的daemon的值
daemon=True 父线程退出该线程也退出
daemon=False 父线程必须等该线程执行完毕。
class Thread:
def __init__(self, group=None, target=None, name=None,
args=(), kwargs=None, *, daemon=None): if daemon is not None:
self._daemonic = daemon
else:
self._daemonic = current_thread().daemon #默认False
Python在构造线程时,daemon参数接收值为True或False,默认为None时,等同于父线程的daemon值。
举例:
import threading
import time
import logging
logging.basicConfig(level=logging.INFO) #警告级别 def worker():
for x in range(100):
time.sleep(1)
msg = ("{} is running".format(threading.current_thread()))
logging.info(msg) t = threading.Thread(target=worker,name='worker-{}'.format(0),daemon=True)
t.start() time.sleep(3)
print('ending') 运行结果:
INFO:root:<Thread(worker-0, started daemon 123145520332800)> is running
INFO:root:<Thread(worker-0, started daemon 123145520332800)> is running
ending
daemon属性为True时,主线程在启动子线程后,继续执行之后语句,主线程执行完毕后,自动退出了所有子线程,主线程也退出。也就是说,当daemon为True时,主线程结束时,子线程无论是否有任务未完成都会随着主线程一起退出。
当daemon为False时也叫non-daemon,主线程运行完毕后,如果有子线程没有退出,就会等待子线程执行完毕,才会退出主线程,整个程序也就结束了。
嵌套的daemon线程:
import threading
import logging
logging.basicConfig(level=logging.INFO) #警告级别
import time def worker():
for x in range(10):
time.sleep(0.3)
msg = ("{} {} is running".format(x,threading.current_thread()))
logging.info(msg) #worker不等worker1
t = threading.Thread(target=worker1,name="worker1-{}".format(x),daemon=True)
t.start()
print(x,'~~~~~~~~~~~~~~~~~~~~~')
# t.join() def worker1():
for x in range(1000):
time.sleep(0.3)
msg = ("¥¥¥¥¥{} is running".format(threading.current_thread()))
logging.info(msg) t = threading.Thread(target=worker,name='worker-{}'.format(0),daemon=False) #主线程等待worker
t.start() # 主 子 孙
# t.join() # False False True
time.sleep(1)
print('ending')
print(threading.enumerate())
这里很容易绕晕,直接看结论把。
小结:
主线程 子线程 子子线程
False False True
主线程
启动子线程
子进程也启动子线程
再傻瓜点理解就是层次最深的线程的daemon影响整个程序的执行结果,如果没有设置daemon,则取它的父线程的daemon。 下一篇详细介绍。
小结:
non-daemon (False) 主线程会等待非daemon线程执行完毕才退出,所以工作完整性请使用non-daemon线程。
daemon (True) 主线程结束,子线程也会结束。适用于不关心什么时候启动,什么时候结束,丢失一部分数据也不关心的场景。例如:服务、心跳包、监控场景应用居多。
join()方法:
正常场景,主线程在起了一个新的子线程后,主线程和子线程是并行的,互不干扰。但是,假如主线程调用了join方法,那它就得等待子线程完全执行完毕才能执行join()之后的语句,相当于又变成了单线程按顺序执行。
举例:
import threading
import time def worker():
for i in range(5):
time.sleep(0.5)
print('working....') t = threading.Thread(target=worker)
t.start()
# t.join() #没有使用join()时的运行情况
print('ending.') 运行结果:
ending.
working....
working....
working....
working....
working....
上面例子中,先打印了主线程的“ending.”语句,说明没有使用join()方法时,主线程线运行完毕,等待子线程也运行完毕才退出程序。
import threading
import time def worker():
for i in range(5):
time.sleep(0.5)
print('working....') t = threading.Thread(target=worker)
t.start()
t.join() #使用join()方法时
print('ending.') 运行结果:
working....
working....
working....
working....
working....
ending.
上面例子中,程序先打印的是子线程的语句,子线程运行完毕后,才打印了主线程的“ending.”语句,也就是使用了join()方法后,又变成了面向过程的程序,后一个语句必须要等前一个语句执行完毕才能执行。
Python 多线程 线程安全、daemon简介 (四)的更多相关文章
- 说说Python多线程中的daemon属性方法
大家看多线程部分的时候肯定看到过daemon这个属性,当我在百度了一圈后也没发现有比较好的解释(或者大家对这个解释都非常清楚),于是自己通过代码和官方介绍了解它,进行了一些总结 给大家一些参考. 首先 ...
- [Python 多线程] 详解daemon属性值None,False,True的区别 (五)
本文以多个例子介绍Python多线程中daemon属性值的区别. 回顾: 前面的文章简单介绍了在现代操作系统中,每一个进程都认为自己独占所有的计算机资源. 或者说线程就是独立的王国,进程间是相对独立的 ...
- python多线程--线程同步
如果多个线程共同对某个数据修改,则可能出现不可预料的结果,为了保证数据的正确性,需要对多个线程进行同步. 使用Thread对象的Lock和Rlock可以实现简单的线程同步,这两个对象都有acquire ...
- Python多线程-线程锁
多线程修改一份数据时需要用到线程锁,以防止数据修改出错 #-*- coding:utf-8 -*- __author__ = "MuT6 Sch01aR" import threa ...
- Python多线程,线程死锁及解决,生产者与消费者问题
1.Thread类 普通调用 t = Thread(target=test, args=(i,)) # test为目标函数名, 若函数需要参数将其以元组形 # 式赋给args, 若无参数可不写 t.s ...
- Python数据分析Numpy库方法简介(四)
Numpy的相关概念2 副本和视图 副本:复制 三种情况属于浅copy 赋值运算 切片 视图:链接,操作数组是,返回的不是副本就是视图 c =a.view().创建a的视图/影子和切片一样都是浅cop ...
- Python多线程多进程那些事儿看这篇就够了~~
自己以前也写过多线程,发现都是零零碎碎,这篇写写详细点,填一下GIL和Python多线程多进程的坑~ 总结下GIL的坑和python多线程多进程分别应用场景(IO密集.计算密集)以及具体实现的代码模块 ...
- Java 多线程(四)之守护线程(Daemon)
目录 定义 如何创建 判断 注意事项 函数setDaemon(true)必须在 start() 函数之前使用. 守护线程中产生的线程也是守护线程: 测试 @ 定义 Java 中有两种线程: 一种是用户 ...
- 进程,线程,GIL,Python多线程,生产者消费者模型都是什么鬼
1. 操作系统基本知识,进程,线程 CPU是计算机的核心,承担了所有的计算任务: 操作系统是计算机的管理者,它负责任务的调度.资源的分配和管理,统领整个计算机硬件:那么操作系统是如何进行任务调度的呢? ...
随机推荐
- shutil的一些基本用法
import shutil import time import tarfile # 将文件内容拷贝到另一个文件中 shutil.copyfileobj(open('a1', 'r'), open(' ...
- 使用装饰器减少try ...finally的重复使用
@util.try_except_bskgk def added_user_handle(cur, search_time): added_user_sql = """ ...
- PoPo数据可视化周刊第一期
PoPo数据可视化 聚焦于Web数据可视化领域, 发现前端可视化领域有意思的内容. 涵盖前端可视化领域最新资讯, 开源可视化库的发布更新消息, 可视化案例分析与讲解, 可视化技术文章, 可视化大神的日 ...
- OI中的一些模板
线性筛 #include"cstdio" #include"cstring" #include"iostream" #include&quo ...
- 01--CSS的引入方式及CSS选择器
一 CSS介绍 现在的互联网前端分三层: a.HTML:超文本标记语言.从语义的角度描述页面结构. b.CSS:层叠样式表.从审美的角度负责页面样式. c.JS:JavaScript .从交互的角度描 ...
- [转]web使用Quartz.NET实现作业调度
转自 https://www.cnblogs.com/best/p/7658573.html 一.Quartz.NET介绍 Quartz.NET是一个强大.开源.轻量的作业调度框架,是 OpenSym ...
- html5 转义实体字符 元数据 跳转 全局属性 id class lang style
实体 Html 实体就是把特殊字符通过代码显示出来, 比如, <>在浏览器会识别为标签,不能正常显示, 这是你就需要安如<去表达左尖括号. 元数据 2. 声明字符编码 3.模 ...
- 关于META你知道多少
META标签,是HTML语言head区的一个辅助性标签.在几乎所有的page里,我们都可以看 到类似下面这段html代码: -------------------------------------- ...
- 【js编程艺术】 之有用的函数
学习js的过程中有几个有用的函数. //添加事件函数 function addLoadEvent(func) { var oldonload = window.onload; if(typeof wi ...
- 浏览器根对象window之performance
W3C性能小组引入的新的API,目前IE9以上的浏览器都支持. 为了解决当前性能测试的困难,W3C推出了一套性能API标准,各种浏览器对这套标准的支持如今也逐渐成熟起来.这套API的目的是简化开发者对 ...