#模拟抢票没有进程锁
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模型
  • 本质:让生产数据和消费数据的效率达到平衡
  • 本质: 把原本获取数据 和 处理数据的过程进行了 解耦
  • 如果你的代码是一堆,不分类,不分函数,成为紧耦合
  • 程序崇尚,高内聚,低耦合
posted on 2020-08-12 18:14  94小渣渣  阅读(82)  评论(0)    收藏  举报