并发编程
1、并发编程
'''
操作系统的发展史
输入输出设备>>>:IO操作即(input和output)
- 手工操作穿孔卡片
- 批处理(磁带)
- 脱机批处理系统
一步步的优化,其实都是在提高计算机CPU利用率的问题(问题在于时串行并且没有空间上的复用)
'''
2、多道技术
# 多道技术的产生
'''
解决cpu在执行程序,遇到io时,不干活的情况
串行:一个程序完完整整的运行完毕,才能运行下一个程序
并发:看上去像同时运行
'''
# 多道技术:
'''
1.空间上的复用(多个程序共一套硬件设备,它是多道技术实现时间上的复用的基础,不然还要去硬盘读数据)
2.时间上的复用(单个cpu的电脑上,起多个应用程序。cpu快速切换,给人的感觉是同时运行)
3.一个任务占用cpu时间过长或被操作系统强行剥夺走cpu的执行权限(比起串行效率反而降低)
4.一个任务执行过程中遇到io操作,也会被操作系统强行剥夺走cpu的执行权限(比起串行效率提高)
'''
3、进程理论
-
什么是进程?
-
程序就是一堆代码
-
进程就是正在运行的程序
注:同一个程序打开两次就是两个进程
-
-
并发与并行
-
并发:看起来像是同时运行,单个cpu+多道技术就可以实现并发,(并行也属于并发)
-
并行:同时运行,只有具备多个cpu才能实现并行
-
4、同步\异步\阻塞\非阻塞
# 同步和异步:针对任务的提交方式
同步:提交任务之后原地等待任务的返回结果,期间不做任何事
异步:提交任务之后,不等待任务的返回结果,执行下一行代码
# 阻塞和非阻塞
阻塞:遇到IO操作
非阻塞:就绪或者运行状态
5、进程的状态
6、进程的创建方式
-
multiproces模块
multiprocessing模块用来开启子进程,并在子进程中执行我们定制的任务(比如函数),该模块与多线程模块threading的编程接口类似。
-
Process 类介绍
Process([group [, target [, name [, args [, kwargs]]]]]),由该类实例化得到的对象,表示一个子进程中的任务(尚未启动)
强调:
1. 需要使用关键字的方式来指定参数
2. args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号
# 参数介绍
1 group参数未使用,值始终为None
3 target表示调用对象,即子进程要执行的任务
5 args表示调用对象的位置参数元组,args=(1,2,'egon',)
7 kwargs表示调用对象的字典,kwargs={'name':'egon','age':18}
9 name为子进程的名称
# 方法介绍
1 p.start():启动进程,并调用该子进程中的p.run()
2 p.run():进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法
4 p.terminate():强制终止进程p,不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。如果p还保存了一个锁那么也将不会被释放,进而导致死锁
5 p.is_alive():如果p仍然运行,返回True
7 p.join([timeout]):主线程等待p终止(强调:是主线程处于等的状态,而p是处于运行的状态)。timeout是可选的超时时间,需要强调的是,p.join只能join住start开启的进程,而不能join住run开启的进程
# 属性介绍
1 p.daemon:默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随之终止,并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置
2
3 p.name:进程的名称
5 p.pid:进程的pid
6
7 p.exitcode:进程在运行时为None、如果为–N,表示被信号N结束(了解即可)
8
9 p.authkey:进程的身份验证键,默认是由os.urandom()随机生成的32字符的字符串。这个键的用途是为涉及网络连接的底层进程间通信提供安全性,这类连接只有在具有相同的身份验证键时才能成功(了解即可
- 创建进程的方式
# 方式1:(函数方法)
from multiprocessing import Process
import time
def task(name):
print('%s is running' %name)
time.sleep(3)
print('%s is over' %name)
if __name__ == '__main__':
p1 = Process(target=task,args=('king',))# args:是元组类型,注意
p1.start()
print('主')
# 方式2:(类方法)
from multiprocessing import Process
import time
class MyProcess(Process):
def __init__(self,name):
super().__init__()
self.name=name
# 重写run方法
def run(self):
print('%s is runnig'%self.name)
time.sleep(3)
print('%s is over' %self.name)
if __name__ == '__main__':
obj = MyProcess('egon')
obj.start()
print('主')
- join 方法
from multiprocessing import Process
import time
def task(name,n):
print('%s is running'%name)
time.sleep(n)
print('%s is over'%name)
if __name__ == '__main__':
start_time = time.time()
p_list = []
for i in range(3):
p = Process(target=task,args=('子进程%s'%i,i))
p.start()
p_list.append(p)
for i in p_list:
i.join()
# p1 = Process(target=task,args=('egon',1))
# p2 = Process(target=task,args=('jason',2))
# p3 = Process(target=task,args=('kevin',3))
# start_time = time.time()
# p1.start() # 千万要知道,这句话只是告诉操作系统需要进程
# p2.start()
# p3.start()
# # p1.join() # 主进程等待子进程结束 才继续运行
# p3.join()
# p1.join()
# p2.join()
print('主',time.time()-start_time)
# join的作用仅仅只是让主进程等待子进程的结束,不会影响子进程的运行
- 守护进程
from multiprocessing import Process
import time
def task(name):
print('%s 正活着'%name)
time.sleep(3)
print('%s 正常死亡'%name)
if __name__ == '__main__':
p = Process(target=task,args=('egon总管',))
p.daemon = True # 必须在p.start开启进程命令之前声明
p.start()
print('皇帝jason正在死亡')
-
进程之间的通讯
进程彼此之间互相隔离,要实现进程间通信(IPC)
# 利用队列实现进程间通信
from multiprocessing import Queue
q = Queue(5) # 产生一个最多能够存放五个数据的队列
# q.put(1) # 往队列中存放数据,如果存放的数据个数大于队列最大存储个数,程序会阻塞
# q.put(2)
# q.put(3)
# print(q.full())
q.put(4)
# q.put(5)
# print(q.full())
# for i in range(6):
# q.put(i)
# 存取数据
for i in range(5):
q.put(i) # for循环往队列里面存放数据
print(q.get()) # 取数据,get一次就取一个
print(q.get())
print(q.get())
q.get_nowait() # 在队列有数据的情况下,跟get取值一样,当队列没有数据的情况下,取值直接报错
print(q.empty()) # 判断队列是否为空,需要注意的是,在并发的情况下,这个方法判断不准确!
print(q.get())
print(q.get())
print(q.empty())
# q.get_nowait()
# print(q.get()) # 如果队列为空,get会在原地等待队列中有数据过来
- 生产者消费者模型
"""
生产者消费者模型
生产者:做包子的 生产数据的
消费者:买包子的 处理数据的
解决供需不平衡的问题
定义一个队列,用来存放固定数量的数据
解决一个生产者和消费者不需直接打交道,两者都通过队列实现数据传输
Queue:管道+锁
"""
from multiprocessing import Queue,Process,JoinableQueue
import time
import random
def producer(name,food,q):
for i in range(5):
data = '%s生产了%s%s'%(name,food,i)
time.sleep(random.randint(1,3))
print(data)
q.put(data) # 将生产的数据放入队列中
def consumer(name,q):
while True:
data = q.get()
if data is None:break
time.sleep(random.randint(1, 3))
print('%s吃了%s'%(name,data))
q.task_done() # 告诉你的队列,你已经将数据取出并且处理完毕
if __name__ == '__main__':
q = JoinableQueue() # 生成一个队列对象
p1 = Process(target=producer,args=('大厨egon','包子',q))
p2 = Process(target=producer,args=('靓仔tank','生蚝',q))
c1 = Process(target=consumer,args=('吃货owen',q))
c2 = Process(target=consumer,args=('坑货kevin',q))
p1.start()
p2.start()
c1.daemon = True
c2.daemon = True
c1.start()
c2.start()
# 等待生产者生产完所有的数据
p1.join()
p2.join()
# 在生产者生产完数据之后,往队列里面放一个提示性的消息,告诉消费者已经没有数据,你走吧,不要等了
# q.put(None)
# q.put(None)
q.join() # 等待队列中数据全部取出
print('主')