6.python之进程
什么是进程
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。
进程的特征:
动态性:进程的实质是程序在多道程序系统中的一次执行过程,进程是动态产生,动态消亡的。 并发性:任何进程都可以同其他进程一起并发执行 独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位; 异步性:由于进程间的相互制约,使进程具有执行的间断性,即进程按各自独立的、不可预知的速度向前推进 结构特征:进程由程序、数据和进程控制块三部分组成。 多个不同的进程可以包含相同的程序:一个程序在不同的数据集里就构成不同的进程,能得到不同的结果;但是执行过程中,程序不能发生改变。
进程与程序的区别:
程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。
而进程是程序在处理机上的一次执行过程,它是一个动态的概念。
并发与并行
并发:伪并行,看着像同时运行,其实是任务之间的切换(遇到IO切换的会提高代码效率),任务切换+报错状态(保存现场)
并行:真正同时运行,应用的是多核技术(多个cpu)
同步\异步\阻塞\非阻塞
进程的三状态:就绪(等待操作系统调度去cpu里面执行) 执行 阻塞
同步:多个任务提交出去要排队一个任务执行完后才执行下一个是串行的
异步 :任务的提交方式,多个任务提交出去,同时执行
阻塞与非阻塞:
阻塞和非阻塞这两个概念与程序(线程)等待消息通知(无所谓同步或者异步)时的状态有关。也就是说阻塞与非阻塞主要是程序(线程)等待消息通知时的状态角度来说的
同步阻塞
效率最低
异步阻塞
异步操作是可以被阻塞住的,只不过它不是处理消息时阻塞,而是等待消息通知时阻塞
同步非阻塞形式
实际上效率低下
程序要在两种不同的行为之间来回切换
异步非阻塞形式
效率高
程序不需要在两种不同的操作之间来回切换
multiprocess模块
Process类中参数的介绍
group参数未使用,值始终为None target表示调用对象,即子进程要执行的任务 args表示调用对象的位置参数元组,args=(1,2,'egon',) kwargs表示调用对象的字典,kwargs={'name':'egon','age':18} name为子进程的名称
from multiprocessing import Process import time def f1(): time.sleep(2) print("xxx") def f2(): time.sleep(2) print("kkk") #windows系统下必须写main,因为windows系统创建子进程的方式决定的,开启一个子进程, #这个子进程 会copy一份主进程的所有代码,并且机制类似于import引入,这样就容易导致引入代码的时候, #被引入的代码中的可执行程序被执行,导致递归开始进程,会报错 if __name__ == '__main__': p1 = Process(target=f1,) p2 = Process(target=f2,) p1.start() p2.start()
Process类中方法介绍:
p.start():启动进程,并调用该进程中的p.run()
p.run():进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类中一定要实现这方法
p.termintate(): 强制终止进程p,不会进行任何清理操作
p.is_alive(): 如果p仍然运行,返回True
p.join(): 主进程等待子进程运行完才继续执行,p.join只能join住start开启的进程,而不能join住run开启的进程
# -*- coding:utf-8 -*- import time from multiprocessing import Process def f1(): time.sleep(2) print("xxx") def f2(): time.sleep(2) print("sss") if __name__ == '__main__': p1 = Process(target=f1,) p1.start() p1.join()# 主进程等待子进程运行完才继续执行 print("开始") p2 = Process(target=f2,) p2.start() p2.join() print("我是主进程!!!")
from multiprocessing import Process def f1(n): print(n) if __name__ == '__main__': # p1 = Process(target=f1,args=("alex",)) p1 = Process(target=f1,kwargs={'n':'alex'}) p1.start()
from multiprocessing import Process class MyProcess(Process): def __init__(self,n): super().__init__() #执行父类的init self.n = n def run(self): print("wusir和%s在一起"%self.n) if __name__ == '__main__': p1 = MyProcess("alex") p1.start()
Process类中自带封装的各属性
p.daemon:默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随之终止,并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置
p.name:进程的名称
p.pid:进程的pid
windows中的Process()必须放到if __name__ == '__main__':下
没有写就会不断的递归调用模块,导致报错
创建进程的第二种方法(继承)
from multiprocessing import Process class MyProcess(Process): def __init__(self,n): super().__init__() self.n = n def run(self): print("%s和alex不可告人的密码"%self.n) if __name__ == '__main__': p1= MyProcess("wusir") p1.start()
from multiprocessing import Process n = 100 def work(): global n n = 0 print('子进程:',n)#0 if __name__ == '__main__': p = Process(target=work,) p.start() p.join() print("主进程内",n)#100
Process对象其他方法或属性
from multiprocessing import Process import time import random class Piao(Process): def __init__(self,name): self.name = name super().__init__() def run(self): print("%s is piaoing" %self.name) time.sleep(random.randrange(1,5)) print("%s is piaoing" %self.name) p1 = Piao("alex") p1.start() print(p1.is_alive()) #True p1.terminate() #关闭进程,不会立即关闭,所以is_alive立刻查看的结果可能还是存活 time.sleep(0.2) print("开始") print(p1.is_alive()) #False
from multiprocessing import Process import time import random class Piao(Process): def __init__(self,name): super().__init__() self.name = name def run(self): print("%s is piaoing"%self.name) time.sleep(random.randrange(1,3)) print("%s is paioing" %self.name) if __name__ == '__main__': p = Piao('alex') p.start() print("开始") print(p.pid)
僵尸进程:当子进程比父进程先结束,而父进程又没有回收子进程,释放子进程占用的资源,此时子进程将成为一个僵尸进程
孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
守护进程
一.守护进程会在主进程代码执行结束后终止
二.守护进程无法再开启子进程
p.deamon = True #一定要在p.start()前添加
# -*- coding:utf-8 -*- import time from multiprocessing import Process def f1(): time.sleep(3) print('守护进程的代码') def f2(): time.sleep(5) print("普通进程的代码") if __name__ == '__main__': p = Process(target=f1,) p.daemon = True #设置为守护进程 p.start() p2 = Process(target=f2,) p2.start() #等待p2进程结束,才进行执行下面的代码 # p2.join() #守护进程会跟着父进程代码的结束而结束 print("主程序结束")
进程同步锁
加锁可以保证多个进程修改同一块数据时,同一时间只能有一个任务可以进行修改,串行的修改,牺牲了速度确保证了数据安全
用文件共享数据实现进程间通信存在的问题:
1.效率低(数据在硬盘上)
2.需要自己加锁处理
from multiprocessing import Process,Lock import time def f1(i,lic): lic.acquire() time.sleep(1) print(i) lic.release() if __name__ == '__main__': lic = Lock() for i in range(20): p = Process(target=f1,args=(i,lic)) p.start()
加锁模拟抢票
# -*- coding:utf-8 -*- #先创建一个ticket.txt文件,写入{'count':1} import time from multiprocessing import Process,Lock def show_t(i): with open('ticket','r',encoding='utf-8') as f: ticket_data = f.read() t_data = eval(ticket_data) print("%s查询剩余票数为%s"%(i,t_data['count'])) def get_t(i,l1): l1.acquire() with open('ticket','r',encoding='utf-8') as f: ticket_data = f.read() t_data = eval(ticket_data) if t_data['count'] > 0: t_data['count'] -= 1 print("%s抢票成功"%i) time.sleep(0.2) with open('ticket','w') as f: f.write(str(t_data)) else: print('没票了!!!') l1.release() if __name__ == '__main__': l1 = Lock() for i in range(10): p1 = Process(target=show_t,args=(i,)) p1.start() for i in range(10): p2 = Process(target=get_t,args=(i,l1)) p2.start()
队列
进程彼此之间互相隔离,要实现进程间通信(IPC),multiprocessing模块支持两种形式:队列和管道,这两种方式都是使用消息传递的。队列就像一个特殊的列表,但是可以设置固定长度,并且从前面插入数据,从后面取出数据,先进先出。
1 队列和管道都是将数据存放于内存中
2 队列基于(管道+锁)实现,
from multiprocessing import Process ,Queue q = Queue(3) q.put(1) print(">>>>>:",q.qsize())#返回当前队列的内容长度 print(q.full()) q.put(2) print(">>>>>:",q.qsize()) q.put(3) print(q.full()) try : q.put_nowait(4) #queue.Full except: print("满了") print(q.get()) print(q.get()) print(q.get()) print("是不是不空",q.empty()) try: q.get_nowait() #queue.Empty except: print("队列空了") print("那多了")
q = Queue([maxsize]) 创建共享的进程队列。maxsize是队列中允许的最大项数。
q.get() 返回q中的一个项目。如果q为空,此方法将阻塞,直到队列中有项目可用为止
q.get_nowait( ) 不等待,会报错
q.put_nowait( ) 不等待,会报错
q.put() 将item放入队列。如果队列已满,此方法将阻塞至有空间可用为止。
q.qsize() 返回队列中目前项目的正确数量。
q.empty() 如果调用此方法时 q为空,返回True。
q.full() 如果q已满,返回为True. 由于线程的存在,结果也可能是不可靠的。
from multiprocessing import Process,Queue def f1(q): q.put("约吗") if __name__ == '__main__': q=Queue(3) p = Process(target=f1,args=(q,)) p.start() son_p_msg = q.get() print("来自进程的消息:",son_p_msg)
Manager模块
# -*- coding:utf-8 -*- import time from multiprocessing import Process,Lock,Manager def f1(m_d,l1): with l1: tmp = m_d['num'] tmp -= 1 time.sleep(0.1) m_d['num'] = tmp if __name__ == '__main__': m = Manager() l1 = Lock() m_d = m.dict({'num':100}) p_list = [] for i in range(10): p = Process(target=f1,args=(m_d,l1)) p.start() p_list.append(p) [pp.join() for pp in p_list] print(m_d['num'])
生产者消费者模型
在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。
# -*- coding:utf-8 -*- from multiprocessing import Queue,Process import time def producer(q): for i in range(10): time.sleep(0.7) s = '大包子%s号'%i print(s+'新鲜出炉') q.put(s) def consumer(q): while 1: time.sleep(1) baozi = q.get() print(baozi+"被吃了") if __name__ == '__main__': q = Queue(10) pro_p = Process(target=producer,args=(q,)) con_p = Process(target=consumer,args=(q,)) pro_p.start() con_p.start()
# -*- coding:utf-8 -*- from multiprocessing import Queue,Process import time def producer(q): for i in range(10): time.sleep(0.7) s = '大包子%s号'%i print(s+'新鲜出炉') q.put(s) q.put(None) def consumer(q): while 1: time.sleep(1) baozi = q.get() if baozi == None: print("都吃完了") break print(baozi+"被吃了") if __name__ == '__main__': q = Queue(10) pro_p = Process(target=producer,args=(q,)) con_p = Process(target=consumer,args=(q,)) pro_p.start() con_p.start()
# -*- coding:utf-8 -*- from multiprocessing import Queue,Process import time def producer(q): for i in range(10): time.sleep(0.7) s = '大包子%s号'%i print(s+'新鲜出炉') q.put(s) def consumer(q): while 1: time.sleep(1) try: baozi = q.get_nowait()#不合适,因为无法确定做的块,还是吃的块,如果按照这样的写法,你吃的快的话,那么这个消费者的程序就直接结束了,不能满足需求 except Exception: break print(baozi+"被吃了") if __name__ == '__main__': q = Queue(10) pro_p = Process(target=producer,args=(q,)) con_p = Process(target=consumer,args=(q,)) pro_p.start() con_p.start()
# -*- coding:utf-8 -*- import time from multiprocessing import Process,Queue,JoinableQueue def producer(q): for i in range(10): time.sleep(0.2) s = "大包子%s号"%i print(s+"新鲜出炉") q.put(s) q.join() #就等着task_done()信号的数量,和我put进去的数量相同时,才继续执行 print("所有数据处理完毕") def consumer(q): while 1: time.sleep(0.3) baozi = q.get() print(baozi+"被吃了") q.task_done() #给队列发送一个取出的这个任务已经处理完毕的信号 if __name__ == '__main__': q = JoinableQueue(30) pro_p = Process(target=producer,args=(q,)) con_p = Process(target=consumer, args=(q,)) pro_p.start() con_p.daemon = True con_p.start() pro_p.join() print("主进程结束")
JoinableQueue([maxsize])
maxsize是队列中允许最大项数,缩略无大小限制
q.task_done() 消费者使用此方法发出信号,表示q.get()的返回项目已经被处理了,如果调用此方法的次数大于从队列中删除项目的数量,将引发ValueError异常
q.join() 生产者调用此方法进行阻塞,直到队列中所有项目被处理.阻塞将持续到队列中的每个项目均调用q.task_done()方法为止
from multiprocessing import Process,JoinableQueue import time,random,os def consumer(q): while True: res = q.get() time.sleep(random.randrange(1,3)) print('\033[45m%s 吃 %s\033[0m' %(os.getpid(),res)) q.task_done() def producer(name,q): for i in range(10): time.sleep(random.randrange(1,3)) res='%s%s' %(name,i) q.put(res) print('\033[44m%s 生成了 %s\033[0m' %(os.getpid(),res)) q.join() if __name__ == '__main__': q = JoinableQueue() p1 = Process(target=producer, args=('包子',q)) p2 = Process(target=producer, args=('馒头', q)) p3 = Process(target=producer, args=('alex', q)) c1 = Process(target=consumer,args=(q,)) c2 = Process(target=consumer,args=(q,)) c1.deamon = True c2.deamon = True p_1 = [p1,p2,p3,c1,c2] for p in p_1: p.start() p1.join() p2.join() p3.join() print("主程序")
管道
进程间的通信
Pipe([duplex]) 在进程之间创建一条通道,管道默认全双工的
conn1.recv() 接收conn2.send(obj)发送的对象.
conn1.send(obj) 通过连接发送对象
conn1.close() 关闭连接
from multiprocessing import Process,Pipe def f1(conn): from_qian = conn.recv() print("我是子进程") print("来自主进程的消息:",from_qian) if __name__ == '__main__': conn1,conn2 = Pipe()#创建一个管道对象,全双工,返回管道的两端,但是一端发送的消息,只能另外一端接收,自己这一端是不能接收的 # 可以将一端或者两端发送给其他的进程,那么多个进程之间就可以通过这一个管道进行通信了 p1 = Process(target=f1,args=(conn2,)) p1.start() conn1.send('小宝贝') print("主进程")
信号量
import time import random from multiprocessing import Process,Semaphore def f1(i,s): s.acquire() print("%s男嘉宾到了"%i) time.sleep(random.randint(1,3)) s.release() if __name__ == '__main__': s = Semaphore(4) #计数器4,acquire一次减一,为0,其他人等待,release加一 for i in range(10): p = Process(target=f1,args=(i,s)) p.start()
事件
# -*- coding:utf-8 -*- from multiprocessing import Process,Event e = Event() print("e的状态",e.is_set()) e.set() #将e的状态改为True print("e的状态",e.is_set()) e.clear() #将e的转态改为False e.wait() #e这个事件对象如果为False,就在我加wait的地方等待 print("进程过了wait")
进程池
p.apply() #同步方法 p.apply_async() #异步方法 p.close() 关闭进程池,防止进一步操作 p.join() 等待所有工作进程退出
import time from multiprocessing import Process,Pool # def f1(n): # time.sleep(1) # print(n) #对比多进程和进程池的效率 def f1(n): for i in range(5): n = n + i if __name__ == '__main__': #统计进程池进行100个任务的时间 s_time = time.time() pool = Pool(4) #里面这个参数是指定进程池中有多少个进程用的,4表示4个进程,如果不传参数,默认开启的进程数一般是cpu的个数 # pool.map(f1,[1,2]) #参数必须是可迭代的 pool.map(f1,range(100)) #参数数据必须是可迭代的,异步提交任务时自带join功能 e_time = time.time() dif_time = e_time - s_time #统计100个进程,来执行100个任务的时间 p_s_t = time.time()#多进程起始时间 p_list = [] for i in range(100): p = Process(target=f1,args=(i,)) p.start() p_list.append(p) [pp.join() for pp in p_list] p_e_t = time.time() p_dif_t = p_e_t - p_s_t print("进程池的时间:",dif_time) print('多进程的执行时间:',p_dif_t)
# -*- coding:utf-8 -*- import time from multiprocessing import Process,Pool def f1(n): time.sleep(1) return n*n if __name__ == '__main__': pool = Pool(4) for i in range(10): print("xxxx") res = pool.apply(f1,args=(i,)) print(res)
# -*- coding:utf-8 -*- import time from multiprocessing import Process,Pool def f1(n): time.sleep(0.5) return n*n if __name__ == '__main__': pool = Pool(4) res_list = [] for i in range(10): print("xxxx") #异步给进程池提交任务 res = pool.apply_async(f1,args=(i,)) res_list.append(res) # print("等待所有任务执行完") # pool.close() #锁住进程池,意思就是不让其他的程序再往这个进程池里面提交任务了 # pool.join() # 打印结果,如果异步提交之后的结果对象 for i in res_list: print(i.get())
回调函数
# -*- coding:utf-8 -*- import os from multiprocessing import Pool,Process def f1(n): print('进程池里面的进程id',os.getpid()) print('>>>>',n) return n*n def call_back_func(f): print(">>>>>>>>>>>",os.getpid()) print("回调函数的结果",f) if __name__ == '__main__': pool = Pool(4) res = pool.apply_async(f1,args=(5,),callback=call_back_func) pool.close() pool.join() print('子进程的id',os.getpid())