进程相关
内容概要
一、僵尸进程、孤儿进程、守护进程、进程其他方法
二、互斥锁
三、队列
四、生产者消费者模型
1、僵尸进程、孤儿进程
僵尸进程,就是进程结束了,但是资源未被回收。这种现象出现在所有进程中,因为进程虽然要结束,但也要保留一些信息提供给父进程来访问(比如执行时间)
孤儿进程,子进程执行过程中父进程突然死亡,子进程无法被回收。
-操作系统有机制会处理这类孤儿进程
守护进程,守护进程的出现避免了孤儿进程的产生
-设置守护进程:只要在申请开启进程前设置
-进程对象.daemon = True即可
from multiprocessing import Process import time def task(): print('妃子还活着') time.sleep(1) print('妃子正常死亡') if __name__ == '__main__': p = Process(target=task,) p.daemon = 1 p.start() print('皇帝驾崩了,手下理应都死')
注意:一般主进程会在子进程都执行接受后,才会回收资源。而守护进程是在主进程执行代码结束后立即死亡。
进程相关(与运维相关)
-类似端口,每一个进程在申请时都有一个独有的id号,即PID。
可以通过两种方式获取进程PID号
-方式一:利用current_process().pid获取当前进程的pid号
-方式二:利用os模块的os.getpid方法,还可以使用os.getppid方法获取父进程的PID号(推荐)
from multiprocessing import Process, current_process import os def task(): print(current_process().pid) # 获取子进程pid号 print(os.getppid()) if __name__ == '__main__': p = Process(target=task, ) p.start() print(os.getpid()) # 获取当前进程的pid号 print(os.getppid()) # 获取当前进程的父进程,即pycharm的pid号
可以在命令行或者终端中查看和筛选进程号
-windows系统下
-在命令行输入tasklist查看,tasklist |findstr PID号查看进程详情
-mac系统下
-在终端中输入ps aux查看,ps aux|grep PID号查看进程详情
2、互斥锁
多进程能提高cpu使用效率,但是在程序运行过程中也会带来一些问题,其中数据混乱会是很大的问题
互斥锁可以解决这个问题,它通过牺牲程序运行效率,保证了数据的安全,互斥锁要尽可能少用,而且理应在执行数据读写入时才使用
数据混乱实例
创建一个json文件,其中存储了{"ticket_num":1}的数据
模拟抢票程序
from multiprocessing import Process import json import time import random def search(i): with open('data',mode='r',encoding='utf-') as f: dic = json.load(f) print('用户{},剩余票数:{}'.format(i,dic.get('ticket_num'))) def buy(i): # 服务端加载数据 with open('data', mode='r', encoding='utf-8') as f: dic = json.load(f) ticket_num = dic.get('ticket_num') # 模拟网络延迟 time.sleep(random.randrange(1,3)) # 数据发送到客户端 if ticket_num > 0: ticket_num -= 1 dic['ticket_num'] = ticket_num with open('data', mode='w', encoding='utf-8') as f: json.dump(dic,f) print('用户{}抢票成功'.format(i)) else: print('用户{}抢票失败'.format(i)) def run(i): search(i) buy(i) if __name__ == '__main__': for i in range(6): p = Process(target=run,args=(i,)) p.start()
利用互斥锁解决数据错乱问题
from multiprocessing import Process,Lock import json import time import random def search(i): with open('data',mode='r',encoding='utf-') as f: dic = json.load(f) print('用户{},剩余票数:{}'.format(i,dic.get('ticket_num'))) def buy(i,lock): lock.acquire() # 上锁 with open('data', mode='r', encoding='utf-8') as f: dic = json.load(f) ticket_num = dic.get('ticket_num') time.sleep(random.randrange(1,3)) if ticket_num > 0: ticket_num -= 1 dic['ticket_num'] = ticket_num with open('data', mode='w', encoding='utf-8') as f: json.dump(dic,f) print('用户{}抢票成功'.format(i)) else: print('用户{}抢票失败'.format(i)) lock.release() # 解锁 def run(i,lock): search(i) buy(i,lock) if __name__ == '__main__': lock = Lock() # 创建一把锁 for i in range(6): p = Process(target=run,args=(i,lock,)) p.start()
利用Lock类创建一把锁对象后,将锁传给进程要执行的函数中
lock.acquire方法和lock.release方法之间的代码同时有且只有一个进程可以执行,这期间其它进程会阻塞在lock.acquire这行代码中
3、队列
队列是存储数据的方式、除此之外还有管道和堆栈
-管道 subprocess模块 stdout、stderr管道
-队列中的数据是 先进先出 它是管道+锁
-堆栈中的数据是 后进先出
进程之间数据是彼此隔离的,但是可以通过队列实现数据的交换
from multprocessing import Queue模块使用队列
q = Queue() 实例化对象,可以传参,参数设置队列中能存放的数据的最大值
q.put()往队列中存放数据。当队列中数据满了,再放数据,会发生程序阻塞
q.get()往队列中拿取数据。当队列中数据为空,再拿数据,会发生程序阻塞
q.get(timeout=3)往队列中拿取数据。当队列中数据为空,等待timeout设定得事件后,会报错
q.get_nowait()往队列中拿取数据。当队列中数据为空,再拿数据,会报错
q.full()判断队列是否已满
q.empty()判断队列是否为空
注意:q.empty()、q.full()、q.get_nowait()在多进程中并不准确,例如使用q.empty时,也许前脚检测结果为空,后脚就有其他进程往队列中添加了数据
Queue更多只会运用在测试中,实际生产中,会用到功能更强大的消息队列,比如:
-redis
-kafka
-RQ
4、生产者消费者模型
什么是生产者消费者模型呢?
-生产者指的就是生产数据的进程
-消费者指的是将生产者生产的数据拿到并进行处理的进程
-生产者和消费者之间应该加入一个桥梁,它就是队列
ps:为了提高数据生产和处理的速度,不应该将生产数据和处理数据的代码写道统一文件中,只用主线程执行代码,按逻辑处理,那样会拖慢效率
最理想的生产者和消费者模型应该是生产者生产了一个数据,消费者会马上处理这个数据;而且生产者生产了多少数据,消费者就消费多少数据
模型示范1:
from multiprocessing import Queue,Process import time import random def producer(q,i): time.sleep(random.randrange(2)) data = '{}生产了蛋糕'.format(i) print(data) q.put(data) def consumer(q): while 1: time.sleep(random.randrange(2)) data = q.get() if data is None: break print('消费了{}'.format(data)) if __name__ == '__main__': l = [] # 创建队列 q = Queue() # 开启生产者进程 for i in range(10): p = Process(target=producer,args=(q,i,)) p.start() l.append(p) # 开启消费者进程 for i in range(2): p = Process(target=consumer,args=(q,)) p.start() # 保证生产者生产完数据 for x in l: x.join() # 往队列中添加用于结束一个消费者进程的标识,这里用到的标识是None,有多少消费者就得放入多少结束标识符 q.put(None) q.put(None)
模型实例2:
from multiprocessing import Process,JoinableQueue import time import random def producer(q,i): time.sleep(random.randrange(2)) data = '{}生产了蛋糕'.format(i) print(data) q.put(data) # 每当执行一次put代码,q中的数据计数器+1 def consumer(q): while 1: time.sleep(random.randrange(2)) data = q.get() if data is None: break print('消费了{}'.format(data)) q.task_done() # 每次手动执行task_done命令,q中的数据计数器-1 if __name__ == '__main__': l = [] # 创建队列 q = JoinableQueue() # 开启生产者进程 for i in range(10): p = Process(target=producer,args=(q,i,)) p.start() l.append(p) # 开启消费者进程 for i in range(2): p = Process(target=consumer,args=(q,)) # 将所有消费者进程设置为守护进程 p.daemon = 1 p.start() # 保证生产者生产完数据 for x in l: x.join() # 使用q.join()方法,当q中的数据计数为0时,才会放行代码 q.join() # 由于主进程执行完毕,所有的守护进程(即消费者进程都会结束)
生产者消费模型在实际生活中的例子:最典型的就是python的爬虫,从网页抓取数据是生产者,将得到的数据进行快速处理的是消费者
ps: IPC机制(不清楚是啥)
from multiprocessing import Process,Queue import time
# 研究思路 # 1、主进程跟子进程借助于队列通信 # 2、子进程跟子进程借助于队列通信 def producer(q):
time.sleep(0.1) q.put('我是23号技师,很高兴为您服务') print('hello big baby') def consumer(q): print(q.get()) if __name__ == '__main__': q = Queue(5) p1 = Process(target=producer,args=(q,)) p2 = Process(target=consumer,args=(q,)) p2.start() p1.start() # print(q.get())
本文来自博客园,作者:口乞厂几,转载请注明原文链接:https://www.cnblogs.com/laijianwei/p/14426827.html

浙公网安备 33010602011771号