线程也是一种多任务编程方法,可以利用计算机多核资源完成程序的并发执行。线程又被称为轻量级的进程。
* 线程计算机多核分配的最小单位
* 一个进程可以包含多个线程
* 线程也是一个运行的过程,消耗计算机资源,多个线程共享进程的资源和空间
* 线程的创建删除消耗的资源都要远远小于进程
* 多个线程之间执行互不干扰
* 线程也有自己的特有属性,比如指令集 ID
threading.Thread()
功能 : 创建线程对象
参数 :name 线程名称 默认 Thread-1
target 线程函数
args 元组 给线程函数位置传参
kwargs 字典 给线程函数键值传参
t.start() 启动线程 自动运行线程函数
t.join([timeout]) 回收线程
t.is_alive() 查看线程状态
t.name 线程名称
t.setName() 设置线程名称
t.getName() 获取线程名称
threading.currentThread() 获取当前线程对象
t.daemon 属性
默认情况主线程退出不会影响分支线程执行
如果设置为True 则分支线程随主线程退出
设置方法:
t.daenon = True
t.setDaemon(True)
判断属性值
t.isDaemon()
* 要在start前设置,不会和join同用
步骤:
1.继承Thread
2.加载Thread中的__init__
3.重写run方法
通信方法: 多个线程共享进程的空间,所以线程间通信使用全局变量完成。
注意事项: 线程间使用全局变量往往要同步互斥机制保证通信安全
线程的event
e = threading.Event() 创建事件对象
e.wait([timeout]) 如果e为设置状态则不阻塞否则阻塞
e.set() 将e变为设置状态
e.clear() 清除设置
线程锁
lock = threading.Lock() 创建锁对象
lock.acquire() 上锁
lock.release() 解锁
* 也可以通过with上锁,上锁状态调用acquire会阻塞
python--》支持多线程--》同步互斥--》加锁--》
超级锁,给解释器加锁--》解释器同一时刻只能解释一个线程
后果 : 一个解释器同一时刻只能解释执行一个线程,所以导致python线程效率低下。但是当遇到IO阻塞时线程会主动让出解释器,因此python线程更加适合高延迟的IO程序并发。
解决方法:
* 尽量用进程完成并发
* 不适用c解释器 c# java
* 尽量使用多种方案组合的方式进行并发操作,线程用作高延迟IO
效率测试
Line cpu: 9.014907121658325
Line IO: 4.548823118209839
thread cpu: 9.38966417312622
thread IO: 4.6143529415130615
Process cpu: 5.466824531555176
Process IO: 2.9468178749084473
1. 两者都是多任务编程方式,都能够使用计算机的多核资源
2. 进程的创建删除消耗的计算机资源比线程要多
3. 进程空间独立,数据相互不干扰,有专门的IPC,线程使用全局变量进行通信
4. 一个进程可以创建多个线程分支,两者之间存在包含关系
5. 多个线程公用进程的资源,在资源操作时往往需要同步互斥
6. 进程线程在系统中都有自己特有的属性,ID,代码段,栈区等资源
使用场景
* 需要创建较多并发,同时任务关联性比较强时一般用多线程
* 不同的任务模块可能更多使用进程
* 使用进程线程需要考虑数据的处理复杂度,比如进程间通 信是否方便,同步互斥是否过于复杂
要求 :
1. 进程线程的区别和联系
2. 进程间通信方式都知道哪些,有什么特点
3. 同步互斥意义是什么,什么情况下用
4. 给一个情形,分析下用进程还是用线程,理由
5. 一些常见概念挖掘 : 僵尸进程, 进程状态,GIL
司机和售票员的故事
* 创建父子进程分别代表司机和售票员
* 当售票员收到SIGINT信号,给司机发送SIGUSR1信号此时司机打印"老司机开车了"
当售票员收到SIGQUIT信号,给司机发送SIGUSR2信号此时司机打印"车速有点快,系好安全带"
当司机捕捉到SIGTSTP信号,给售票员发送SIGUSR1,售票员打印"到站了,请下车"
* 到站后 售票员先下车,司机下车 (子进程先退出)
想要看更多的课程请微信关注SkrEric的编程课堂