Python的线程,,Python中的线程


Python中的线程

进程会启动一个解释器进程,线程共享一个解释器进程.

Python的线程开发

Python的下线程开发使用标准库threading模块

Thread类

参数名含义
target线程调用的对象,就是目标函数
name为线程起个名字(不重要,可以重名,就是给人看看)
args为目标函数传递实参,args=元组
kwargs为目标函数传关键字函数,kwargs=字典

菜鸟https://www.runoob.com/python3/python3-multithreading.html

Python的线程没有优先级,没有线程组的概念,也不能被销毁,停止,挂起,那也就没有恢复,中断了.

线程的启动

通过threading.Thread创建一个线程对象,target是目标函数,name可以指定名称

但是线程没有启动,需要调用start方法.

线程之所以执行函数,是因为线程中就是执行代码的,而最简单的封装就是函数,所以还是要函数调用,

函数执行完毕,线程就退出了

线程退出

Python中没有提供线程退出的方法,在一下情况时退出

1.线程函数内语句执行完毕

2.线程函数中抛出未处理的异常

Python中的线程没有优先级,没有线程组的概念,也不能被销毁,终止,挂起,也就没有恢复,中断

线程的传参

线程传参和函数传参没什么区别,本质上就是函数传参.

线程对象的参数列表中

args=(元组)

kwargs={字典} ---基本不用

threading模块的属性和方法

名称含义
main_thread()返回主线程==对象==
current_thread()返回当前线程==对象==
active_count()当前处于alive状态的线程个数
enumerate()返回所有活着的线程的列表,不包括已经终止的线程和未开始的线程
get_ident()返回当前线程ID,非0整数

Thread实例的属性和方法

名称含义
name这是一个线程的名字,一个标识,可以重名.getName(),setName()获取,设置这个名词
ident线程id,是一个非0整数,线程启动后才会有id,否则为None,线程退出,此id依旧可以访问,id可重复使用
is_alive()返回线程是否活着

注意:线程的name是一个名称,可以后重复;==ID必须唯一==,但可以在线程退出后回收再利用.

名称含义
start()启动线程,每一个线程必须且只能执行该方法一次
run()运行线程函数

start()方法会调用run()方法,而run()方法可以运行函数.

一个线程只能有一个start,除非再实例化一个线程

区别:start方法启动线程,启动了一个新的线程;但是run方法,只是调用了一个函数

? run方法只是一个函数调用,调用线程函数

? ==因此,启动线程要使用start方法,才能启动多个线程.==

多线程

多个线程,一个进程中如果有多个线程,就是多线程,实现一种并发

threading模块中,如果没有开新的进程,那就是普通函数的调用,所以执行完t1.run().然后执行t2.run(),这里就不是多线程.

当使用start方法启动线程后,进程内有多个活动那个的线程并行的工作,这就是多线程.

一个进程中至少有一个线程,并作为程序的入口,这个线程i就是==主线程==,

一个进程至少有一个主线程.其他线程称为工作线程.

线程安全

线程执行一段代码,不会产生不确定的结果,那这段代码就是线程安全的

类或者函数或者方法分为线程安全和线程不安全两种

解决:

1: 字符串是不可变类型,它可以作为一个整体不可分割输出,在字符串上做文章

2:使用logging,标准库里面的logging模块,日志处理模块,它是线程安全的,生产环境代码都使用logging

daemon线程可non-daemon线程

注意:这里的daemon不是Linux中的守护进程

进程靠线程执行代码,至少有一个主线程,其他线程是工作线程.

主线程是第一个启动的线程,

父线程:如果线程A中启动了一个线程B,A就是B的父线程.

子线程:B就是A的子线程

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

线程的daemon属性,主线程的daemon默认false,所以主线程是个non-daemon线程

总结:

daemon一定要在start方法之前定义

线程具有一个daemon属性,可以显示设置为True或False,也可以不设置,默认取None.

如果不设置,就取当前线程的daemon来设置它.

主线程是non-daemon线程,即daemon = False

从主线程创建的所有线程不设置daemon属性,默认值为False,也就是non-daemon线程

Python程序在没有或者的non-daemon线程运行时退出,剩下的就只能是daemon线程,主线程才能退出,否则主线程就只能等待.

当主线程退出时,如果还有存活的non-daemon线程,也不会干掉所有daemon线程,而是直到所有non-daemon线程全部结束,如果还有daemon线程,主线程需要退出,会结束所有daemon线程,退出.

daemon线程应用场景

当一个线程被设置成daemon,它会随主线程的退出而退出.简化了手动关闭线程的工作.

主要应用场景:

后台任务,如发送心跳包,监控,这种场景最多主线程工作才有用的线程,如主线程种维护着公共资源,主线程已经清理了,准备退出,而工作线程使用这些资源工作也没有意义了,一起退出最合适随时可以被终止的线程

如果在non-daemon线程A中.对另一个daemon线程B使用呢了join方法,这个线程B设置成daemon就没有意义了,因为non-daemon线程A总是要等待B

如果在一个daemon线程C中,对另一个daemon线程D使用了join方法,只能说明C要等待D,主线程退出,C,D不管结束否,也不管谁等谁,都要被干掉.

join

join(timeout = None) 是线程的标准方法之一

一个线程中调用另一个线程的join方法,调用者将被阻塞,直到被调用线程终止,

一个线程可以被join多次

timeout参数指定调用者等待多久,没有设置超时,就一直等到被调用线程结束

调用谁的join方法,就是join谁,就要等谁

threading.local类

threading.local类,将这个类实例化得到一个全局变量,但是不同的线程使用这个对象存储的数据,其他线程看不见,被放到了线程各自的字典中.

threading.local类构建了一个大字典,其元素是每一个线程实例的地址为key和线程对象引用线程单独的字典映射

{id(thread) -->(ref(thread),thread-local dict)} ref为引用

通过threading.local实例就可以在不同的线程中,安全的使用线程独有的数据,做到了线程间数据隔离,就像本地变量一样安全.

先实例化一个ctx = threading.local()对象,各个线程各自调用ctx.x = 100;ctx.y= 200

在谁的线程里定义,就在谁的线程中使用

定时器Timer

threading.Timer继承自Thread,用来定义多久执行一个函数

class threading/Timer(interval,function,args=None,kwarg=None)

start方法执行之后,Timer对象会处于等待状态,等待了interval之后,开始执行function函数

如果在执行函数之前的等待阶段,使用了cancel方法,就会跳过执行函数结束.

但是如果已经执行,则cancel无效

Python的线程

评论关闭