#模拟抢票没有进程锁
from multiprocessing import Process,Lock
import json
import time
def bye_ticket(n,lock1):
with open('ticket.info') as f1:
dict1 = json.load(f1)
if dict1['count']>0:
print(f'{n}进程查到余票:{dict1["count"]}')
time.sleep(0.1)
dict1['count']-=1
with open('ticket.info',mode='w') as f2:
json.dump(dict1,f2)
print(f'{n}进程买到票了!')
else:print(f'{n}进程查询,没有余票!')
time.sleep(0.2)
if __name__ == '__main__':
lock1 = Lock()
for i in range(5):
p = Process(target=bye_ticket,args=(i,lock1))
p.start()
# 0进程查到余票:2
# 1进程查到余票:2
# 2进程查到余票:2
# 3进程查到余票:2
# 4进程查到余票:2
# 0进程买到票了!
# 1进程买到票了!
# 2进程买到票了!
# 3进程买到票了!
# 4进程买到票了!
#模拟抢票,加进程锁
from multiprocessing import Process,Lock
import json
import time
def bye_ticket(n,lock1):
lock1.acquire() #还可以替换成 with lock: 一句就行
with open('ticket.info') as f1:
dict1 = json.load(f1)
if dict1['count']>0:
print(f'{n}进程查到余票:{dict1["count"]}')
time.sleep(0.1)
dict1['count']-=1
with open('ticket.info',mode='w') as f2:
json.dump(dict1,f2)
print(f'{n}进程买到票了!')
else:print(f'{n}进程查询,没有余票!')
time.sleep(0.2)
lock1.release()
if __name__ == '__main__':
lock1 = Lock()
for i in range(5):
p = Process(target=bye_ticket,args=(i,lock1))
p.start()
# 0进程查到余票:2
# 0进程买到票了!
# 1进程查到余票:1
# 1进程买到票了!
# 2进程查询,没有余票!
# 3进程查询,没有余票!
# 4进程查询,没有余票
from multiprocessing import Process,Lock
import time
import json
import random
def search():
dic = json.load(open('db'))
print('\033[43m剩余票数%s\033[0m'%dic['count'])
def get():
dic = json.load(open('db'))
time.sleep(0.1)#模拟读数据网络延迟
if dic['count']>0:
dic['count']-=1
time.sleep(0.2)#模拟写数据网络延迟
json.dump(dic,open('db','w')) #原来打开文件这么简单?w代表只写
print('\033[43m购票成功\033[0m')
def task(lock1):
search()
lock1.acquire()
get()
lock1.release()
if __name__ == '__main__':
lock1 = Lock()
for i in range(10):
p = Process(target=task,args=(lock1,))
p.start()
#加锁可以保证多个进程修改同一块数据时,同一时间只能有一个任务进行修改
#缺点:效率低,串行,而且需要自己处理
#于是multiprocessing提供了IPC通信机制:队列和管道
#队列和管道都是讲数据存放于内存中
#队列给予管道+锁的方式实现
#队列multiprocess.Queue
#进程间通信IPC(Inter-Process Communication)
from multiprocessing import Queue
q = Queue(3)
q.put(1)
q.put(2)
q.put(3)
print(q.get()) #1先进先出
print(q.get()) #2
print(q.get_nowait()) #3 get和get_nowait效果一样,都是取数据
# 队列满了 put_nowait 不用等待,不阻塞,但是会报错,异常,同样队列空了,再取也会报错
from multiprocessing import Queue
q = Queue(5)
for i in range(10):
try:
q.put_nowait(i)
except:
print('满了')
break
for i in range(10):
try:
print(q.get_nowait())
except:
print('空了')
break
print(q.empty()) #True
#队列的简单应用:进程间通信
import time
from multiprocessing import Process,Queue
def f(q,n):
print('子进程开始')
q.put([time.asctime(), 'tom', 'hello'])
n = 10
time.sleep(2)
print('子进程结束')
if __name__ == '__main__':
print('主进程开始')
n = 100
q = Queue()
p = Process(target=f,args=(q,n))
p.start()
print(q.get()) #这里取得队列的值,是子进程写入的,如果换成整数变量n试一下呢?
print(n) #定义一个整数变量,子进程修改成啥,主进程的都不变。。
p.join() #等待子进程运行完
print('主进程结束')
# 主进程开始
# 子进程开始 (子进程开始,对q队列写入一个列表)
# ['Wed Aug 12 15:29:58 2020', 'tom', 'hello'] (在子进程sleep的时候,主进程读取了队列)
# 子进程结束 (子进程才结束,主进程和子进程同时使用了队列资源)
# 主进程结束
#进程用队列通信的例子
import os
import time
import multiprocessing
def inputQ(q):
q.put(f'{os.getpid()}子进程say hello')
print(os.getpid(),'写信息到队列')
def outputQ(q):
print(f'{os.getpid()}子进程获取信息【{q.get()}】')
if __name__ == '__main__':
multiprocessing.freeze_support() #还不知道有啥用?
p_list_input=[]
p_list_output=[]
q = multiprocessing.Queue(3) #队列总共能放3个元素
for i in range(10): #10个入队列的子进程
p = multiprocessing.Process(target=inputQ,args=(q,))
p.start()
p_list_input.append(p)
for i in range(10): #10个出队列的子进程
p = multiprocessing.Process(target=outputQ, args=(q,))
p.start()
p_list_output.append(p)
for p in p_list_input:p.join() #join到底是什么好处?经常要写,当一个子进程结束,主进程就要马上处理的情形
for p in p_list_output:p.join()
# 11672 写信息到队列
# 5172 写信息到队列
# 1744 写信息到队列
# 7044子进程获取信息【11672子进程say hello】
# 10832 写信息到队列
# 7360子进程获取信息【5172子进程say hello】
# 9804 写信息到队列
# 11920子进程获取信息【1744子进程say hello】
# 7692 写信息到队列
# 13052子进程获取信息【10832子进程say hello】
# 13036 写信息到队列
# 12836子进程获取信息【9804子进程say hello】
# 8272 写信息到队列
# 11800子进程获取信息【7692子进程say hello】
# 2892 写信息到队列
# 10184子进程获取信息【13036子进程say hello】
# 8004 写信息到队列
# 13216子进程获取信息【8272子进程say hello】
# 7704子进程获取信息【2892子进程say hello】
# 7648子进程获取信息【8004子进程say hello】
# 先有3个写队列的子进程,写了3个信息到队列,队列只能放3个,第四个写的时候就开始阻塞等待了
# 等取队列的子进程7044运行起来,取走一个,然后,10832入队列的子进程又被调度运行了。
# 后续就是取一个写一个,直到最后连续取走三个 结束
生产者消费者模型
#生产者消费者模型
# 生产者可以生产产品,消费者要消费产品,两个过程最好是独立进行(异步)
# 如果生产者生产的过量,消费者来不及消费,造成资源浪费;
# 如果消费者消费太快,生产者来不及生产,也会降低生产者体验
# 双方需要相互通信,来告诉对方自己生产或者消费的需求,达到供需平衡,比如日常生活中的零售库存管理,包子铺等
#解决方案:
#在系统中安排一个容器(队列)来存放产品,生产者和消费者通过队列来安排自己的生产和消费
#生产者查看队列,如果满的,就不生产
#消费者取队列,如果没有,就不取
#包子铺,客人的到来时随机的,2个子进程,1个厨师做包子进程,1个是消费者吃
from multiprocessing import Process,Queue
import time
import os
import random
def consumer(q):#消费者
while 1:
res = q.get()
time.sleep(random.randint(1,3))
print(f'{os.getpid()}客户吃了{res}')
def producer(q):
for i in range(10): #生产者一天只生产10个
time.sleep()
res = f'包子{i}号'
q.put(res)
print(f'{os.getpid()}进程生产了{res}')
if __name__ == '__main__':
q = Queue() #这里不限制,有可能来一波人等着吃包子
p1 = Process(target=consumer,args=(q,))
p2 = Process(target=producer,args=(q,))
#开始
p1.start()
p2.start()
# 11928进程生产了包子0号
# 11928进程生产了包子1号
# 10784客户吃了包子0号
# 11928进程生产了包子2号
# 10784客户吃了包子1号。。。。。。
# 进程不会结束,因为消费者进程会一直运行(while 1)
# 生产者是有计划生产(根据时间,资源等,1天只生产10个包子),客户却会不停的,随机的来,需要来个打样标志
#包子铺改版,增加结束标志
from multiprocessing import Process,Queue
import time
import os
import random
def consumer(q):
while 1:
res = q.get()
if res is None:break #获取到结束标志
time.sleep(random.randint(1,3))
print(f'{os.getpid()}客户吃了{res}')
def producer(q):
for i in range(10):
time.sleep(random.randint(1,3))
res = f'包子{i}号'
q.put(res)
print(f'{os.getpid()}进程生产了{res}')
q.put(None) #放一个生产结束标志
if __name__ == '__main__':
q = Queue()
p1 = Process(target=consumer,args=(q,))
p2 = Process(target=producer,args=(q,))
#开始
p1.start()
p2.start()
#包子铺再改版,让主进程入队列一个结束标志,那主进程就要在子进程结束的时候做一件事,用join方法
from multiprocessing import Process,Queue
import time
import os
import random
def consumer(q):
while 1:
res = q.get()
if res is None:break #获取到结束标志
time.sleep(random.randint(1,3))
print(f'{os.getpid()}客户吃了{res}')
def producer(q):
for i in range(10):
time.sleep(random.randint(1,3))
res = f'包子{i}号'
q.put(res)
print(f'{os.getpid()}进程生产了{res}')
if __name__ == '__main__':
q = Queue()
p1 = Process(target=consumer,args=(q,))
p2 = Process(target=producer,args=(q,))
#开始
p1.start()
p2.start()
p2.join()
q.put(None)
#包子铺再再改版,如果有2个包子铺一起生产,一天都只生产5个
from multiprocessing import Process,Queue
import time
import os
import random
def consumer(q):
while 1:
res = q.get()
if res is None:break #获取到结束标志
time.sleep(random.randint(1,3))
print(f'{os.getpid()}客户吃了{res}')
def producer(q):
for i in range(5,10):
time.sleep(random.randint(1,3))
res = f'包子{i}号'
q.put(res)
print(f'{os.getpid()}进程生产了{res}')
if __name__ == '__main__':
q = Queue()
p1 = Process(target=consumer,args=(q,))
p2 = Process(target=producer,args=(q,))
p3 = Process(target=producer,args=(q,))
#开始
p1.start()
p2.start()
p3.start()
p2.join()
p3.join()
q.put(None)
#JoinableQueue可连接的共享进程队列 反正没懂
from multiprocessing import Process,JoinableQueue
import time,random,os
def consumer(q):
while 1:
res = q.get()
time.sleep(random.randint(1,3))
print(f'{os.getpid()}进程客户吃了{res}')
q.task_done() #这是 JoinableQueue队列特有的,向q.join()发送信号,说明数据被取走
def producer(name,q):
for i in range(3):
time.sleep(random.randint(1, 3))
res = '%s%s号包子' % (name, i)
q.put(res)
print(f'{os.getpid()}进程生产了{i}号包子')
q.join() #生产完毕,队列阻塞,不是进程阻塞,直到队列所有项目均被处理
if __name__ == '__main__':
q = JoinableQueue()
p1 = Process(target=producer,args=('tom',q,))
p2 = Process(target=producer,args=('jerry',q,))
p3 = Process(target=producer,args=('lucy',q,))
c1 = Process(target=consumer,args=(q,))
c2 = Process(target=consumer,args=(q,))
c1.daemon = True #守护进程,在start之前设置,在主进程代码结束之后就结束
c2.daemon = True
p1.start()
p2.start()
p3.start()
c1.start()
c2.start()
p1.join()
p2.join()
p3.join()
print('主进程代码结束')
进程之间通信 IPC 补充
- 基于文件:同一台机器上多个进程之间的通信
- Queue队列:
- 基于socket的文件级别的通信来完成数据传递,基于pickle Lock 实现,所以他是安全的
- Pipe 管道:基于socket 和 pickle 但是不加锁Lock ,所以不安全
- 基于网络:同一台机器或者多态机器上的多进程之间的通信
- 第三方工具(消息中间件)
- memcache
- redis
- rabbitmq
- kafka
生产者消费者模型 补充
- 爬虫的时候用
- 分布式操作用:celery模型
- 本质:让生产数据和消费数据的效率达到平衡
- 本质: 把原本获取数据 和 处理数据的过程进行了 解耦
- 如果你的代码是一堆,不分类,不分函数,成为紧耦合
- 程序崇尚,高内聚,低耦合