一、昨日内容回顾
如何创建进程
"""进程也可以理解成是内存中某一块存储空间"""
# 针对不同的操作系统代码创建进程的原理有区别
windows以模块导入的方式
linux\mac以拷贝的方式
join方法
主进程代码等待子进程代码运行结束之后再往后执行
p.join()
进程间数据隔离
在同一台计算机上的多个应用程序在内存中是相互隔离的(物理级别隔离)
进程对象属性和方法
1.获取进程号
current_process().pid
os.getpid()
os.getppid()
2.杀死子进程
terminate()
3.判断进程是否存活
is_alive()
僵尸进程与孤儿进程
僵尸进程
进程结束之后不会立刻清空所有信息,会短时间保留进程号等信息等待父进程回收,所有的进程都会经历僵尸进程
父进程回收子进程资源的两种方式
1.父进程正常结束
2.调用join方法
孤儿进程
子进程正常运行 父进程意外死亡(主动杀死>>> taskkill\kill)
守护进程
守护进程会随着主进程的结束立刻结束
p.daemon = True # 将子进程设置为守护进程:主进程结束 子进程立刻结束
p.start()
互斥锁
"""
并发的情况下操作同一份数据 极有可能造成数据错乱的问题
这个时候统一都会采用互斥锁的策略来解决(无论是基础阶段还是后期)
互斥锁:将并发变成串行 牺牲了效率但是保证了数据的安全
以后正常编程很少会出现让我们自己编写代码操作锁的情况!!!
"""
mutex = Lock()
mutex.acquire() # 抢锁互斥锁
mutex.release() # 放锁
二、消息队列
# 由于目前的知识储备还不够直接学习消息队列 所以先学习内置队列
"""
队列:先进先出(使用频率很高)
堆栈:先进后出(特定常见下用)
"""
# 以后我们会直接使用别人封装好的消息队列 实现各种数据传输
from multiprocessing import Queue
q = Queue(5) # 自定义队列的长度
q.put(111)
q.put(222)
q.put(333)
print(q.full()) # False 判断队列是否满了
q.put(444)
q.put(555)
print(q.full()) # True
# q.put(666) # 超出最大长度 原地阻塞等待队列中出现空位
# 程序在阻塞的过程中就会卡住一直执行
print(q.get())
print(q.get())
print(q.empty()) # False 判断队列是否空了
print(q.get())
print(q.get())
print(q.get())
print(q.empty()) # True
# print(q.get()) # 队列中没有值 继续获取则阻塞等待队列中给值
print(q.get_nowait()) # 队列中如果没有值 直接报错
"""
full()
empty()
get_nowait()
上述方法能否在并发的场景下精准使用???
不能用,因为当有多个进程操作队列里面的数据的时候,full(),empty()等方法就有可能刚好失效!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
之所以介绍队列是因为它可以支持进程间数据通信
get()将队列的值取出来了之后再put数据就不会卡住
但是当put的数据数量超过范围get()是不能解决卡住的问题的
"""
# 队列的最大作用就是使得原来数据隔离的进程之间实现数据交互
三、IPC机制(进程间通信)
"""
1.主进程与子进程数据交互
2.两个子进程数据交互
本质:不同内存空间中的进程数据交互
就是专门有个空间拿来存数据和被读取
"""
from multiprocessing import Queue, Process
def producer(q):
# print('子进程producer从队列中取值>>>:', q.get())
q.put('子进程producer往队列中添加值')
def consumer(q):
print('子进程consumer从队列中取值>>>:', q.get())
if __name__ == '__main__':
q = Queue()
p = Process(target=producer, args=(q,))
p1 = Process(target=consumer, args=(q,))
p.start()
p1.start()
print('主进程')
# 没啥好说的,创建两个子进程,一个生产者,一个消费者
四、生产者消费者模型
# 生产者
负责生产/制作数据
# 消费者
负责消费/处理数据
"""
比如在爬虫领域中
会先通过代码爬取网页数据(爬取网页的代码就可以称之为是生产者)
之后针对网页数据做筛选处理(处理网页的代码就可以称之为消费者)
如果使用进程来演示
除了有至少两个进程之外 还需要一个媒介(消息队列)
以后遇到该模型需要考虑的问题其实就是供需平衡的问题
生产力与消费力要均衡
"""
from multiprocessing import Process, Queue, JoinableQueue
import time
import random
def producer(name, food, q):
for i in range(10):
data = f'{name}生产了{food}{i}'
print(data)
time.sleep(random.randint(1, 3)) # 模拟产生过程
q.put(data)
def consumer(name, q):
while True:
food = q.get()
time.sleep(random.random())
print(f'{name}吃了{food}')
if __name__ == '__main__':
q = Queue()
p1 = Process(target=producer, args=('大厨jason', '韭菜炒蛋', q))
p2 = Process(target=producer, args=('学徒tony', '狗不理', q))
p3 = Process(target=producer, args=('老板kevin', '秘制小汉堡', q))
c1 = Process(target=consumer, args=('涛涛', q))
c2 = Process(target=consumer, args=('龙龙', q))
p1.start()
p2.start()
p3.start()
c1.start()
c2.start()
print('主进程')
"""
1.你会发现程序跑到后面会卡住,生产环节的生产食物是有数量上限的,但是程序没跑完
2.生产者代码没到上限卡不住的,是因为消费卡在q.get()这里了,因为拿不到数据
3.不能在子进程中使用q.empty(),因为有多个子进程,一旦队列第一个被吃掉了,子进程就会直接结束
"""
import pickle
from multiprocessing import Process, Queue, JoinableQueue
import time
import random
def producer(name, food, q):
for i in range(10):
data = f'{name}生产了{food}{i}'
print(data)
time.sleep(random.randint(1, 3)) # 模拟产生过程
q.put(data)
def consumer(name, q):
while True:
food = q.get()
if food == None:
print('完蛋了 没得吃了 要饿死人了')
break
time.sleep(random.random())
print(f'{name}吃了{food}')
if __name__ == '__main__':
q = Queue()
p1 = Process(target=producer, args=('大厨jason', '韭菜炒蛋', q))
p2 = Process(target=producer, args=('学徒tony', '狗不理', q))
c1 = Process(target=consumer, args=('涛涛', q))
c2 = Process(target=consumer, args=('龙龙', q))
p1.start()
p2.start()
c1.start()
c2.start()
p1.join()
p2.join()
q.put(None)
q.put(None)
"""
1.解决思路是利用利用join使得在生产完成之后在队列的结尾放上None
2.加上判断使得在迟到None的时候循环直接结束不再执行q.get()
3.但是只有一个None,有两个人要来拿,这个时候就需要有几个人就有几个None
4.这就很麻烦了结束标志需要与消费者数量一致
5.对列中已经自己加了锁,所以多进程不会冲突
"""
from multiprocessing import Process, Queue, JoinableQueue
import time
import random
def producer(name, food, q):
for i in range(5):
data = f'{name}生产了{food}{i}'
print(data)
time.sleep(random.randint(1, 3)) # 模拟产生过程
q.put(data)
def consumer(name, q):
while True:
food = q.get()
# if food == None:
# print('完蛋了 没得吃了 要饿死人了')
# break
time.sleep(random.random())
print(f'{name}吃了{food}')
q.task_done() # 每次取完数据必须给队列一个反馈
if __name__ == '__main__':
# q = Queue()
q = JoinableQueue()
p1 = Process(target=producer, args=('大厨jason', '韭菜炒蛋', q))
p2 = Process(target=producer, args=('老板kevin', '秘制小汉堡', q))
c1 = Process(target=consumer, args=('涛涛', q))
c2 = Process(target=consumer, args=('龙龙', q))
c1.daemon = True
c2.daemon = True
p1.start()
p2.start()
c1.start()
c2.start()
# 生产者生产完所有数据之后 往队列中添加结束的信号
p1.join()
p2.join()
# q.put(None) # 结束信号的个数要跟消费者个数一致才可以
# q.put(None)
"""队列中其实已经自己加了锁 所以多进程取值也不会冲突 并且取走了就没了"""
q.join() # 等待队列中数据全部被取出(一定要让生产者全部结束才能判断正确)
"""执行完上述的join方法表示消费者也已经消费完数据了"""
"""
1.使用q = JoinableQueue()为生产的数据进行计数
2.q.task_done() # 每次取完数据必须给队列一个反馈取一次上面的计数就减少一次
3.q.join()前面必须带着p1.join(),p2.join()因为在生产结束之后才能知道有多少个,才能确定队列中的数据在q.task_done()之后有没有被吃完
4.c1.daemon = True,c2.daemon = True的作用是将吃作为守护进程一旦计数结束就直接不吃了
"""
标签:__,Process,get,队列,知识,print,第四十七,线程,进程
From: https://www.cnblogs.com/tuq2791/p/17465830.html