python进程|线程|协程
进程(Process)
1)进程就是正在运行的程序,它是操作系统中,资源分配的最小单位
(1)资源分配:分配的是cpu和内存等物理资源
(2)进程号是进程的唯一标识
2)同一个程序执行两次之后是两个进程
3)进程和进程之间的关系: 数据彼此隔离,通过socket通信
获取进程id
import os res = os.getpid() # 获取当前进行id (当前子进程) print(res) res = os.getppid() # 获取父进程id print(res) # result """ 13724 13800 """
进程的基本用法
from multiprocessing import Process import time import os def func(): print("S2>>>当前子进程id:%s,它的父进程id:%s" %(os.getpid(),os.getppid())) if __name__ == "__main__": print("S1>>>子进程id:%s,父进程id:%s"%(os.getpid(),os.getppid())) p = Process(target=func) # 创建子进程 target = 函数 单独用一个进程去执行谁,去完成哪个任务 p.start() # 调用子进程 # result """ S1>>>子进程id:7576,父进程id:13800 S2>>>当前子进程id:7100,它的父进程id:7576 """
带有参数的函数
异步程序:不等没一行代码执行完毕,就往下执行其它代码
from multiprocessing import Process import time import os def func(): for i in range(1,5): print("S2>>>当前子进程id:%s,它的父进程id:%s"%(os.getpid(),os.getppid())) if __name__ == "__main__": print("S1>>>子进程id:%s,父进程id:%s"%(os.getpid(),os.getppid())) p = Process(target=func) p.start() n = 5 for i in range(1,n+1): print("*" * i ) # result """ S1>>>子进程id:7928,父进程id:13800 * ** *** **** ***** S2>>>当前子进程id:8112,它的父进程id:7928 S2>>>当前子进程id:8112,它的父进程id:7928 S2>>>当前子进程id:8112,它的父进程id:7928 S2>>>当前子进程id:8112,它的父进程id:7928 """
进程之间数据,彼此隔离
from multiprocessing import Process import time import os count = 99 def func(): global count count += 1 print("当前子进程id:%s"%(os.getpid()),count) if __name__ == "__main__": p = Process(target=func) p.start() time.sleep(1) # 让子进程跑完,看是否通过子进程修改 print("主进程",count) # result """ 当前子进程id:1956 100 主进程 99 """
多进程之间的并发
多程序并发时,因为cpu的调度策略,任务的执行是互相抢占cpu资源的过程
from multiprocessing import Process import time import os def func(args): print("S2>>>当前子进程id:%s,它的父进程id:%s"%(os.getpid(),os.getppid())) print("end>>>",args) if __name__ == "__main__": for i in range(10): Process(target=func,args=(i,)).start() print("主进程执行结束...") # result """ 主进程执行结束... S2>>>当前子进程id:10376,它的父进程id:9932 end>>> 2 S2>>>当前子进程id:13476,它的父进程id:9932 end>>> 1 S2>>>当前子进程id:11812,它的父进程id:9932 end>>> 3 S2>>>当前子进程id:13936,它的父进程id:9932 end>>> 4 S2>>>当前子进程id:2384,它的父进程id:9932 end>>> 7 S2>>>当前子进程id:7024,它的父进程id:9932 end>>> 5 S2>>>当前子进程id:14284,它的父进程id:9932 end>>> 8 S2>>>当前子进程id:4816,它的父进程id:9932 end>>> 9 S2>>>当前子进程id:120,它的父进程id:9932 end>>> 6 S2>>>当前子进程id:8604,它的父进程id:9932 end>>> 0 """
主进程和子进程之间的关系
(1)主进程执行完所有代码,等待子进程全部结束,再终止程序
(2)不等待,子进程变成僵尸用户,在后台不停运行,耗费资源
from multiprocessing import Process import time import os def func(args): print("S2>>>当前子进程id:%s,它的父进程id:%s"%(os.getpid(),os.getppid())) time.sleep(0.1) print("end>>>",args) if __name__ == "__main__": for i in range(10): Process(target=func,args=(i,)).start() print("主进程执行结束...") # result """ 主进程执行结束... S2>>>当前子进程id:10092,它的父进程id:6304 S2>>>当前子进程id:11220,它的父进程id:6304 S2>>>当前子进程id:12992,它的父进程id:6304 S2>>>当前子进程id:8384,它的父进程id:6304 S2>>>当前子进程id:3824,它的父进程id:6304 S2>>>当前子进程id:12880,它的父进程id:6304 S2>>>当前子进程id:6352,它的父进程id:6304 end>>> 0 end>>> 2 S2>>>当前子进程id:7160,它的父进程id:6304 end>>> 9 end>>> 7 S2>>>当前子进程id:6676,它的父进程id:6304 end>>> 5 end>>> 8 end>>> 3 S2>>>当前子进程id:7488,它的父进程id:6304 end>>> 1 end>>> 6 end>>> 4 """
join
等待子进程执行完毕之后,主进程再向下执行
基本用法
from multiprocessing import Process import time import os def func(): print("发送第一封邮件") if __name__ == "__main__": p = Process(target=func) p.start() time.sleep(1) p.join() # 针对于p进程对象来说,必须等待p这个进程任务执行完毕之后,主进程的代码在向下执行 print("发送第二封邮件") # result """ 发送第一封邮件 发送第二封邮件 """
多个子进程
多个子进程通过join加阻塞,可以实现同步控制
from multiprocessing import Process import time import os def func(index): print("第%s封邮件发送..."%(index)) if __name__ == "__main__": lst = [] # 初始化一个列表 for i in range(5): p = Process(target=func,args=(i,)) p.start() lst.append(p) p.join() # 循环列表中每一个进程,都加上join,保证每个子进程执行完毕,保证同步性 for i in lst: i.join() print("发送最后一封邮件...") # result """ 第0封邮件发送... 第1封邮件发送... 第2封邮件发送... 第3封邮件发送... 第4封邮件发送... 发送最后一封邮件... """
自定义类方式创建进程
基本用法
from multiprocessing import Process import time import os class MyProcess(Process): # 必须继承父类Process def run(self): # 必须写成run方法 print(">>>子进程id:%s,父进程id:%s"%(os.getpid(),os.getppid())) if __name__ == "__main__": p = MyProcess() p.start() print(">>>主进程id:%s"%(os.getpid())) # result """ >>>主进程id:9988 >>>子进程id:14248,父进程id:9988 """
带参数子进程函数
from multiprocessing import Process import time import os class MyProcee(Process): def __init__(self,arg): super().__init__() # 必须调用一下父类的构造方法 self.arg = arg def run(self): print(">>>子进程id:%s,父进程id:%s"%(os.getpid(),os.getppid())) print(self.arg) if __name__ == "__main__": lst = [] for i in range(10): p = MyProcee("参数:%s"%(i)) p.start() lst.append(p) for i in lst: i.join() print("最后打印子进程id",os.getppid()) # result """ >>>子进程id:7488,父进程id:8596 参数:6 >>>子进程id:11300,父进程id:8596 参数:8 >>>子进程id:12048,父进程id:8596 参数:4 >>>子进程id:7868,父进程id:8596 参数:0 >>>子进程id:4792,父进程id:8596 参数:5 >>>子进程id:10764,父进程id:8596 参数:2 >>>子进程id:10852,父进程id:8596 参数:1 >>>子进程id:9348,父进程id:8596 参数:3 >>>子进程id:9860,父进程id:8596 参数:7 >>>子进程id:8956,父进程id:8596 参数:9 最后打印子进程id 13800 """
并行和并发
(1)并行:一个cpu同一时间不停执行多个程序
(2)并发:多个cpu同一时间不停执行多个程序
cpu的进程调度方法
(1)先来先服务fcfs(first come first server):先来的先执行
(2)短作业优先算法:分配的cpu多,先把短的算完
(3)时间片轮转算法:每一个任务就执行一个时间片的时间.然后就执行其他的
(4)多级反馈队列算法
越是时间长的,cpu分配的资源越短,优先级靠后,越是时间短的,cpu分配的资源越多
进程三状态图
(1)就绪(Ready)状态-->只剩下CPU需要执行外,其他所有资源都已分配完毕 称为就绪状态。
(2)执行(Running)状态 --> cpu开始执行该进程时称为执行状态
(3)阻塞(Blocked)状态 --> 由于等待某个事件发生而无法执行时,便是阻塞状态,cpu执行其他进程.例如,等待I/O完成input、申请缓冲区不能满足等
同步|异步/阻塞|非阻塞
场景在多任务中
同步:必须等我这件事干完了,你在干,只有一条主线,就是同步
异步:没等我这件事情干完,你就在干了,有两条主线,就是异步
阻塞:比如代码有了input,就是阻塞,必须要输入一个字符串,否则代码不往下执行
非阻塞:没有任何等待,正常代码往下执行
同步阻塞:效率低,cpu利用不充分
异步阻塞:比如socketserver,可以同时连接多个,但是彼此都有recv
同步非阻塞:没有类似input的代码,从上到下执行.默认的正常情况代码
异步非阻塞:效率是最高的,cpu过度充分,过度发热
守护进程
可以给子进程贴上守护进程的名字,该进程会随着主进程代码执行完毕而结束(为主进程守护)
(1)守护进程会在主进程代码执行结束后就终止
(2)守护进程内无法再开启子进程,否则抛出异常(了解)
基本用法
from multiprocessing import Process import time import os def func(): print("子进程start...") time.sleep(0.1) print("子进程end...") if __name__ == "__main__": p = Process(target=func) p.daemon = True # 在start开始前设置该进程为守护进程 p.start() print("主进程执行结束...") # result """ 主进程执行结束... """
多个子进程
(1)当主进程里面的代码全部执行完毕,守护进程自动终止
(2)func1是守护进程,func2是非守护进程
from multiprocessing import Process import time import os def func1(): count = 1 while True: print("*" * count) time.sleep(0.5) count += 1 def func2(): print("func2 start...") time.sleep(2) print("func2 end...") if __name__ == "__main__": p1 = Process(target=func1) p1.daemon = True p2 = Process(target=func2) p1.start() p2.start() time.sleep(1) print("主进程代码执行完毕...") # result """ func2 start... * ** 主进程代码执行完毕... func2 end... """
守护进程应用--报活
from multiprocessing import Process import time import os def alive(): while True: print("一号服务主机ok...") time.sleep(1) # 相隔1秒报活 def func(): print("一号服务主机负责统计日志信息") time.sleep(3) if __name__ == "__main__": p1 = Process(target=alive) p1.daemon = True p1.start() p2 = Process(target=func) p2.start() p2.join() # join添加阻塞,join执行结束,代表服务统计功能失败 print(" ... ") # result """ 一号服务主机ok... 一号服务主机负责统计日志信息 一号服务主机ok... 一号服务主机ok... 一号服务主机ok... ... """
锁(LOCK)
(1)lock.acquire() 上锁
(2)lock.release() 解锁
(3)同一时间允许一个进程上一把锁,就是lock。加锁可以保证多个进程修改同一块数据,同一时间只能有一个任务进行修改,即串行修改,速度慢,保证安全。
(4)同一时间允许多个进程上多把锁,就是信号量[Semaphore]
信号量是锁的变形:实现是 计数器 + 锁,同时允许多个进程上锁
(5)互斥锁Lock:就是进程的互相排斥,谁先抢到资源,谁就上锁修改资源内容,保证数据的同步性
多个锁一起上,不开锁,会造成死锁,上锁和解锁是一对
基本语法
import json,time lock = Lock() # 创建一把锁 lock.acquire() # 上锁 print(123) lock.release() # 解锁
死锁
程序添加了阻塞,程序不能往下执行。下面代码程序阻塞,也不会打印111
lock = Lock() # 创建一把锁 lock.acquire() # 上锁 lock.acquire() print(111) lock.release() # 解锁
抢票示例
from multiprocessing import Lock,Process import time count = 1 def get_ticket(person): global count time.sleep(0.1) if count > 0: print("%s抢到票了..."%(person)) count -= 1 else: print("%s没有抢到票..."%(person)) # 定义一个函数来进行统一调用 def ticket(person,lock): print("%s 查询余票:%s"%(person,count)) # 查询余票 lock.acquire() get_ticket(person) lock.acquire() if __name__ == "__main__": lock = Lock() for i in range(10): p = Process(target=ticket,args=("person%s"%(i),lock)) p.start() # result """ person6 查询余票:1 person0 查询余票:1 person1 查询余票:1 person6抢到票了... person4 查询余票:1 person9 查询余票:1 person3 查询余票:1 person8 查询余票:1 person2 查询余票:1 person7 查询余票:1 person5 查询余票:1 """
信号量
本质上就是锁,只不过可以控制锁的数量
from multiprocessing import Process, Semaphore import os, time def ktv(person, sem): sem.acquire() print("%s进入ktv开始唱歌" % (person)) print(os.getpid()) time.sleep(3) print("%s走出ktv离开歌房" % (person)) sem.release() if __name__ == "__main__": sem = Semaphore(1) for i in range(5): p = Process(target=ktv, args=("person%s" % (i), sem)) p.start() # result """ person3进入ktv开始唱歌 15956 person3走出ktv离开歌房 person2进入ktv开始唱歌 14632 person2走出ktv离开歌房 person4进入ktv开始唱歌 9248 person4走出ktv离开歌房 person1进入ktv开始唱歌 8816 person1走出ktv离开歌房 person0进入ktv开始唱歌 12992 person0走出ktv离开歌房 """
事件(Event)
(1)阻塞事件
e = Event() 生成事件对象e
e.wait() 动态给程序加阻塞,程序当中是否加阻塞完全取决于该对象中的is_set() [默认返回值是False],True不加阻塞
(2)控制这个属性的值
set() 方法:将这个属性的值改成True
clear() 方法:将这个属性的值改成False
is_set() 方法:判断当前的属性是否为True (默认上来是False)
基本语法
from multiprocessing import Process,Event import time,random e = Event() print(e.is_set()) e.wait(1) # 最多阻塞时间为1秒 print(111) # result """ False 111 """
from multiprocessing import Process,Event import time,random e = Event() e.set() # 将内部属性改成True e.wait() print(111) e.clear() # 将内部属性改成False e.wait() print(222) # result """ 111 """
模拟交通灯
(1)两个信号灯会交替变换
from multiprocessing import Process,Event import time,random def traffic_light(e): print("红灯亮") while True: if e.is_set(): time.sleep(1) # 当前绿灯,等待1秒 print("红灯亮") # 等完1秒后,变成红灯 e.clear() else: time.sleep(1) # 当前是红灯 print("绿灯亮") e.set() e = Event() traffic_light(e)
(2)模拟车遇到红灯停,绿灯行的操作
from multiprocessing import Process,Event import time,random def traffic_light(e): print("红灯亮") while True: if e.is_set(): time.sleep(1) # 当前绿灯,等待1秒 print("红灯亮") # 等完1秒后,变成红灯 e.clear() else: time.sleep(1) # 当前是红灯 print("绿灯亮") e.set() def car(e,i): if not e.is_set(): print("car%s在等待..."%(i)) e.wait() print("car%s通行..."%(i)) if __name__ == "__main__": e = Event() lst = [] p1 = Process(target=traffic_light,args=(e,)) p1.daemon = True # 设置守护进程,小车跑完,交通灯终止 p1.start() # 模拟通行 for i in range(5): time.sleep(random.uniform(0,2)) # 随机停0-2秒 p2 = Process(target=car,args=(e,i)) p2.start() lst.append(p2) # 将进程添加到列表中 for i in lst: i.join() # 等所有车跑完,终止交通灯;加一个等待 print("程序结束...") # result """ 红灯亮 绿灯亮 car0通行... 程序结束... 红灯亮 car1在等待... 绿灯亮 car1通行... 程序结束... car2通行... 程序结束... 红灯亮 car3在等待... 绿灯亮 car3通行... 程序结束... car4通行... 程序结束... """
进程间通信IPC
(1)IPC Inter-Process Communication
(2)进程之间通信的两种机制:管道Pipe 队列 Queue
(3)put() 存放
(4)get() 获取
(5)get_nowait() 拿不到报异常
(6)put_nowait() 非阻塞版本的put
(7)q.empty() 检测是否为空
(8)full() 检测是否已经存满
基本语法
(1)先进先出,后进后出
(2)让进程之间形成数据之间的共享
from multiprocessing import Process,Queue q = Queue() q.put(1) # 把数据放到q队列中 res = q.get() # 把数据从队列中取出 print(res) res = q.get() # 没有数据后会形成阻塞 res = q.get_nowait() # 没有数据,直接报错
queue指定队列长度
from multiprocessing import Queue q = Queue(3) q.put(1) q.put(2) q.put(3) # q.put(4) # 存放值满了,阻塞 q.put_nowait(5) # 往队列中存值,满直接报错
进程通信,依赖Queue
from multiprocessing import Queue,Process # 子进程 def func(q): res = q.get() # 子进程获取数据 print(res) q.put(1) # 子进程添加数据 if __name__ == "__main__": q = Queue() p = Process(target=func,args=(q,)) p.start() q.put(2) # 主进程添加数据 p.join() res = q.get() # 主进程添加数据 print(res) print("程序结束...") # result """ 2 1 程序结束... """
生产者与消费者模型
如:1号进程负责爬取网页中内容,2号进程负责匹配提取网页中关键字
1号进程可以看成一个生产者,2号进程可以看成一个消费者
from multiprocessing import Process,Queue import os,random,time # 消费者模型(负责取值) def consumer(q,name): while True: food = q.get() # 拿出数据 if food is None: break time.sleep(random.uniform(0.1,1)) print("%s 吃了一个 %s"%(name,food)) # 生产者模型(负责存值) def producer(q,name,food): for i in range(3): time.sleep(random.uniform(0.1,1)) print("%s 生产了 %s,%s"%(name,food,i)) q.put(food + str(i)) if __name__ == "__main__": q = Queue() c1 = Process(target=consumer,args=(q,"A")) c1.start() p1 = Process(target=producer,args=(q,"B","egg")) p1.start() p1.join() q.put(None) # result """ B 生产了 egg,0 A 吃了一个 egg0 B 生产了 egg,1 A 吃了一个 egg1 B 生产了 egg,2 A 吃了一个 egg2 """
进程池
(1)开启过多的进程并不一定提高效率
cpu负载任务过多,平均单个任务执行效率低,较低执行速度
(2)apply 开启进程,同步阻塞,每次都要等待当前任务完成之后,开启下一个进程
(3)apply_async 开启进程,异步非阻塞(主进程和子进程异步)
线程
(1)进程是资源分配的最小单位,线程是计算机中调度的最小单位
(2)线程的缘起
资源分配需要分配内存空间,分配cpu,分配的内存空间存放着临时要处理的数据,如要执行的代码,数据。
(3)线程的特点
轻量级,能干更多的活,一个进程中所有线程资源共享,一个进程至少一个线程工作
一个进程可以有多个线程,共享一份资源
from threading import Thread from multiprocessing import Process import os,time,random def func(num): time.sleep(random.uniform(0.1,1)) print("子线程",num,os.getpid()) if __name__ == "__main__": for i in range(5): t = Thread(target=func,args=(i,)) t.start() # result """ 子线程 1 6348 子线程 3 6348 子线程 0 6348 子线程 4 6348 子线程 2 6348 """
并发多进程线程对比
from threading import Thread from multiprocessing import Process import os,time,random # 计算多线程时间 def func(i): # print("子线程",i,os.getpid()) pass if __name__ == "__main__": lst = [] start_time = time.time() for i in range(100): t = Thread(target=func,args=(i,)) t.start() lst.append(t) for i in lst: i.join() end_time = time.time() print("time:",end_time-start_time) # result """ time: 0.02393651008605957 """
from threading import Thread from multiprocessing import Process import os,time,random def func(i): # print("子线程",i,os.getpid()) pass if __name__ == "__main__": lst = [] start_time = time.time() for i in range(100): p = Process(target=func,args=(i,)) p.start() lst.append(p) for i in lst: i.join() end_time = time.time() print("time:",end_time-start_time) # result """ time: 13.565200328826904 """
多线程共享同一份进程资源
from threading import Thread from multiprocessing import Process import os,time,random num = 100 lst = [] def func(i): global num num -= 1 for i in range(100): t = Thread(target=func,args=(i,)) t.start() lst.append(t) for i in lst: i.join() print(num) # result """ 0 """
线程相关函数
(1)线程.is_alive() 检测线程是否仍然存在
(2)线程.setName() 设置线程名字
(3)线程。getName() 获取线程名字
(4)currentThread().ident 查看线程id
(5)enumerate() 返回目前正在运行的线程列表
(6)activeCount() 返回目前正在运行的线程数量
查看线程id
from threading import currentThread,Thread import os def func(): print("子线程",currentThread().ident) t = Thread(target=func) t.start() print("主线程",currentThread().ident,os.getpid()) # result """ 子线程 9952 主线程 912 13268 """
enumerate()
from threading import currentThread,Thread,enumerate import os,time def func(): print("子线程",currentThread().ident) time.sleep(0.5) for i in range(5): t = Thread(target=func) t.start() print(enumerate()) print(len(enumerate())) # result """ 子线程 3668 子线程 5856 子线程 13288 子线程 6304 子线程 5496 [<_MainThread(MainThread, started 8644)>, <Thread(Thread-1, started 3668)>, <Thread(Thread-2, started 5856)>, <Thread(Thread-3, started 13288)>, <Thread(Thread-4, started 6304)>, <Thread(Thread-5, started 5496)>] 6 """
activeCount()
from threading import currentThread,Thread,enumerate,activeCount import os,time def func(): print("子线程",currentThread().ident) time.sleep(1) for i in range(5): Thread(target=func).start() print(activeCount()) # result """ 子线程 5168 子线程 3428 子线程 13808 子线程 11732 子线程 2756 6 """
守护线程
等待所有线程执行结束后自动结束,守护所有线程
from threading import currentThread,Thread,enumerate,activeCount import os,time def func1(): while True: time.sleep(1) print("线程1") def func2(): print("线程2-start") time.sleep(2) print("线程2-end") t1 = Thread(target=func1) t1.setDaemon(True) t1.start() t2 = Thread(target=func2) t2.start() print("主线程执行结束...") # result """ 线程2-start 主线程执行结束... 线程1 线程2-end """
线程的数据安全 依赖Lock
from threading import currentThread,Thread,enumerate,activeCount,Lock import os,time n = 0 def func1(lock): global n for i in range(1000): lock.acquire() # 上锁 n -= 1 lock.release() # 解锁 def func2(lock): global n for i in range(1000): with lock: n += 1 if __name__ == "__main__": lock = Lock() lst = [] for i in range(5): t1 = Thread(target=func1,args=(lock,)) t2 = Thread(target=func2,args=(lock,)) t1.start() t2.start() lst.append(t1) lst.append(t2) for i in lst: i.join() print("主线程结束") print(n) # result """ 0 """
信号量
from threading import currentThread,Thread,enumerate,activeCount,Lock,Semaphore import os,time,random def func(i,sem): time.sleep(random.uniform(0.1,1)) with sem: print(i) time.sleep(2) if __name__ == "__main__": sem = Semaphore(5) for i in range(5): Thread(target=func,args=(i,sem)).start() # result """ 2 1 0 4 3 """
死锁,递归锁,互斥锁
线程队列
协程(gevent)
(1)协程也叫纤程,协程是线程的一种实现
一条线程可以在多任务之间来回切换,对于cpu,操作系统协程不存在
(2)协程的实现
协程记住任务执行到那个位置,并且实现安全的切换
一个任务一旦阻塞,立刻切换到另一个任务继续执行,保证线程总是忙碌
(3)一个线程可以由多个协程实现,协程之间不会产生数据安全问题
(4)协程模块
greenlet gevent的底层,协程,切换的模块
gevent 直接用,gevent能提供更全面的功能
def gen(): for i in range(5): yield i mygen = gen() for i in mygen: print(i) # result """ 0 1 2 3 4 """
协程改写生产者消费者模型
def producer(): for i in range(5): yield i def consumer(): g = producer() for i in g: print(i) consumer() # result """ 0 1 2 3 4 """
协程的具体实现
import time from greenlet import greenlet def eat(): print("eat one") g2.switch() time.sleep(3) print("eat two") def play(): print("play one") time.sleep(1) print("playtwo") g1.switch() g1 = greenlet(eat) g2 = greenlet(play) g1.switch() # result """ eat one play one playtwo eat two """
gevent不能识别阻塞
from gevent import monkey # mokey.patch_all 可以把下面引入的所有模块中的阻塞,重新识别出来 monkey.patch_all() import time import gevent def eat(): print("eat one") time.sleep(1) print("eat two") def play(): print("play one") time.sleep(1) print("playtwo") g1 = gevent.spawn(eat) g2 = gevent.spawn(play) g1.join() g2.join() print("主线程执行结束") # result """ eat one play one eat two playtwo 主线程执行结束 """