上一次,创建两个线程,一个主线程,另外新启动的叫工作线程,一般使用 程序,都会有多线程,有的每个线程做不一样的事情,有的是一起做一件事情,并发,多线程是一种并发的解决方案,服务窗口多,用户的请求就很快处理好了
是否会冲突,就跟递归调用函数,函数的栈是线程的,虽然函数调用了两次,但是执行的时候是在各自线程的栈上完成的,线程的栈是自己的,不合其他线程有分享的,所有调用都在这个线程内完成
下面就是在不同的线程上调用了两次函数,是不会互相干扰的,各压各的栈
函数调用是跟线程相关的,所有的函数执行是在线程中执行的,即时是同一个函数也要看在哪一个线程执行
把前面的例子做一下修改
两个start,调用系统调用,会真正创建出线程,创建两个线程,线程yoghurt同一个函数来执行
修改下,表名两个线程打印的应该不一样
是交替执行的
改成run试试
t1的这个函数彻底执行完了才能执行t2,,但是现在没有退出条件,t2的等待就遥遥无期
用run方法,就是使用主线程,没有开启子线程,t1的run的函数如果执行没结束,就回不到主线程继续往下走,是一个串行的过程
用start就不一样了。就是开t1和t2两个线程,主线程需要等工作线程都结束了才能结束
一个程序至少有一个线程,而且这个线程必须是程序的入口,就算一个进程起来也需要有干活的,至少有一个线程,这个线程作为一个约定,入口所进入的线程叫主线程
python运行的哪个模块,哪个模块就会被加载到主线程,是在主线程运行的
这样相当于把t模块跑在主线程中
一个进程至少一个主线程,其他都称为工作进程,刚才调用都是同一个函数,同一个函数运行在不同的线程里,不同线程有自己的栈,这些函数都会在自己的线程上不停的压栈
pycharm演示不出来。使用ipython来演示
有交错现象就是并行的效果,是分时间片的,一会就能打很多条,所以出现交错的现象
查看ipython的交错效果
有了多线程才打印出来这样,print第一件事是打印你给定的值,第二件事就是补打所谓的换行符。换行符是在print的end参数传的
这样就出现了并行,worker-1 is running说明这个worker1线程刚打印出来,正准备换行符的时候,打印了worker3,如果不确定就可以打印换行符换成变量,弄成线程id
worker3打了那么多,然后切换到worker1,补了个换行符,并行的效果确实是在交替做,但是有时候会把你想的切开做,比如下面
print函数,两步是被打断的,这说明print函数是线程不安全,因为使用多线程要注意,这个线程是否安全,如果线程不安全,在使用多线程的时候要特别小心,因为得到的结果可能是你不想看到的东西
(往往得到不可预期的效果就是线程不安全)
什么是线程安全,就是线程执行一段代码后,不会产生不确定的结果,
这样就属于产生不确定结果了,线程不安全的,在线程内竟然被打断了
并行带来的结果就可能达不到你的预期,如果有协调问题,就会出现问题,print函数不是线程安全的,那么能否解决
通过改造代码。来得到一个线程安全的结果,其实print打印的东西并没有被切开
这种方式试试
这样就解决问题了,这样每次都必须把字符串打完
logging,以后日志的输出肯定要做
logging下的info把字符传进去
查看是否会出现类似print的现象
上面打印出来就是logging 的默认级别是警告,info是低于警告的,所以打印不了
这里就不会出现刚才刚才的情况
是走的stderrer,是红色的
这个线程就是安全的,这就是为什么在项目开发中,这些信息用日志,默认错误信息是输出到stderror上的,走的输出,并没有记录到文件中,但这些日志按照一定要求存到日志中,就是要我们去进行日志分析,然后去发现程序出现了什么样的问题,一般写程序都是要有日志输出的
end=’'不打换行符,直接在字符串后面加\n换行符,因为字符串打印不会断开
往往都会使用logging模块,logging模块是线程安全的,在多线程的适合不会出现不可预见的效果,生成环境代码都使用logging
多线程就特别要注意线程安全与否,线程安全会出大问题
线程安全是有代价的,线程不安全的可以不可以规避,比如print添加\N一样,往往可以提高效率
特殊参数daemon
** *星号之后,keyword only,但这里的daemon并不是指linux的守护进程(在后台跑的)**
进程中是靠线程来执行代码,至少有一个主线程,其他线程都是工作线程,主线程是第一个要启动的线程,也就是使用threading.enumrate显示的线程就是能看到的线程,
父子线程,线程A中创建了一个线程B,并且start起来,那么A就是B的父线程,B就是A的子线程
可以改名称和daemon值,这个值的修改必须在start之前,start之后就已经给操作系统发生请求了,给创建一个线程出来,在这之后改就晚了
所以daemon要在start之前就设定,true就是daemon,false就是no daemon
一般情况下做完就start了,一般都会创建对象的时候,传参的时候确定值,一般不会在后面setDaemon
现在按下daemon是做什么的,修改代码,threading中的thread
查看源码如何做的,在创建一个线程对象的初始化中
会做这些参数的处理,如果daemon的缺省值是none,
如果daemon的缺省值是none,如果不是none就用你给的,如果是none,就用当前线程的daemon
之前threading用了两个线程相关的,current_thread,main_thread
就往下一直运行了
工作线程没结束,主线程一直在等
主线程其实在等待工作线程的
现在把daemon值加上,先给false,执行完得等里面工作线程处理完才可以
改成true,工作线程没打印,直接正常退出
当你设定daemon之后,主线程执行完毕后,瞅一眼,如果是deamon=true,直接结束,如果是false,只能是none deamon
如果是none,用当前线程
给none,等价于给false,等于none是从创建它的父线程得到,current_thread,t1还没有start就肯定还是当前的主线程,说明从主线程获得的daemon值是false
主线程启动之后,它一定是个none daemon,从它这里创建的线程的daemon值,如果不设定,都是直接从这里获取,就是false,所以主线程就得被迫等子线程,因为daemon值是false,主线程只能等待
主线程退出的时候需要看到有没有none daemon线程,如果有,就只能等待none daemon线程执行结束,然后才能说工作完成
但是如果主线程看子线程,有nonedaemon,也有非none daemon
先简单改造一下,启动5个线程,全是daemon=true
没有看到im working,还没来得及打印,主线程就结束了,整个进程就退出了,主线程是none deamon的,不要修改,如果看到其他的非主线程是deamon的,就不会再做任何等待,直接退出
试试这样
全部都有了
加个名字看的清楚一点
也就是引入了一个none daemon的线程之后,按照期望的来做
放在前面也一样,因为python线程特殊
全是daemon的,主线程是不等的
现在弄两个线程,一个none daemon的,一个daemon
执行看看
none daemon执行完了,主线程局部等下面的睡5秒的daemon线程了
还是不等
在主线程运行结束的时候,会查看当前进程中有没有none daemon
改成这样
当主线程执行完的时候,会看一眼当前进程中是否还有none daemon线程,而创建线程的daeamon值就要看父进程的daemon值
所谓的daemon和none daemon,主线程要结束的时候会看一下,当前进程中是否还存在none daemon的线程,如果有一个none daemon,都必须等,再看一眼,发现所有的none daemon完成了,不管有多少个daemon线程,都会结束当前进程
上面没执行完,主线程就一直在等
daemon值建议在创建的时候就定义好,必须在start前,start已经创建好了,再改就来不及了,所以setDaemon要在daemon前,isdaemon就要判断线程是否是daemon
就靠这个内部的变量来判断。_代表保护变量
deamon是线程具有的一个属性,如果不设置,会从父线程拿daemon值,也就是当前线程的daemon值。
主线程默认none daemon,主线程下的所有子线程,不设置属性,都是从主线程获得daemon值,也就是none-daemon,
python程序会在没有活着的non-daemon线程运行时退出,但是这个none-daemon是除了主线程之外的,但凡有一个工作线程还是none-daemon的话,这个程序就是不会退出的,主线程退出,整个程序也就退出了
到这里才能启动一个none-daemon线程
下面的主线程看到none-daemon启动就退出不了
等待两秒,上面的就肯定创建none-daemon线程
none-daemon如果久的话,那么daemon进程一定会运行完,反之,daemon进程久可能不能完全运行完
主线程在退出的时候,一定会看除自己意外是否还会有none-daemon线程,如果有,会继续等待,没有就退出,主线程退出,整个进程退出