主要知识点:互斥锁、队列和生产者消费者模型

一、互斥锁

 1、进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终端,是没有问题的,

而共享带来的是竞争,竞争带来的结果就是错乱。

  如下实例: 

#!/usr/bin/env python3
#-*- coding:utf-8 -*-
# write by congcongfrom multiprocessing import Process,Lock
import timedef task(name):print('%s 1'%name)time.sleep(1)print('%s 2'%name)time.sleep(1)print('%s 3'%name)if __name__ == '__main__':for n in range(1,3):p = Process(target=task,args=('进程%d'%n,))p.start()'''输出:
进程1 1
进程2 1
进程1 2
进程2 2
进程1 3
进程2 3
'''

  那么,如何控制呢?答案就是加锁处理。

  2、互斥锁的理解:

    互斥锁的意思就是互相排斥,如果把多个进程比喻为多个人,互斥锁的工作原理就是多个人都要去争抢

  同一个资源:比如卫生间,一个人抢到卫生间后上一把锁,其他人都要等着,等到这个完成任务后释放锁,其他

  人才有可能有一个抢到,如此反复。

  3、互斥锁的原理:

    把并发改成串行,降低了效率,但保证了数据安全不错乱

    互斥锁的实例如下:   

# Lock 互斥锁保证了进程间的有序运行,但效率降低
def task(name,mutex):mutex.acquire() # 加锁(添加互斥锁)print('%s 1'%name)time.sleep(1)print('%s 2'%name)time.sleep(1)print('%s 3'%name)mutex.release()  # 释放互斥锁,再运行其他进程if __name__ == '__main__':mutex = Lock()   # 父进程实例化一个互斥锁,子进程都会拷贝一份,需要传递给子进程for i in range(1,3):p = Process(target=task,args=('进程%d'%i,mutex))p.start()
''' 输出:
进程1 1
进程1 2
进程1 3
进程2 1
进程2 2
进程2 3
'''

  4、互斥锁的实际应用--模拟抢票程序

   1、包括两个文件,分别是:模拟抢票.py 和 data.json文件。 

    模拟抢票.py 

#-*- coding:utf-8 -*-
# write by congcong
import json
import time
from multiprocessing import Process,Lockdef search(name):time.sleep(1)dic = json.load(open('data.json','r',encoding='utf-8'))print('<%s>查看剩余票数 【%d】'%(name,dic['count']))def buy(name):time.sleep(1)dic = json.load(open('data.json','r',encoding='utf-8'))if dic['count'] > 0:dic['count'] -= 1time.sleep(2)json.dump(dic,open('data.json','w',encoding='utf-8'))print('<%s> 购票成功!'%name)def task(name,mute):search(name) # 查询不需要加锁,可以并发执行mute.acquire() # 加锁,获得锁的进程才能继续运行,串行执行
    buy(name)mute.release() # 解锁if __name__ == '__main__':mute = Lock()for i in range(1,4):p = Process(target=task,args=('路人%d'%i,mute))p.start()
'''未加互斥锁时输出:
<路人2>查看剩余票数 【2】
<路人1>查看剩余票数 【2】
<路人3>查看剩余票数 【2】
<路人2> 购票成功!
<路人1> 购票成功!
<路人3> 购票成功!'''
'''加互斥锁后输出:
<路人1>查看剩余票数 【2】
<路人2>查看剩余票数 【2】
<路人3>查看剩余票数 【2】
<路人1> 购票成功!
<路人2> 购票成功!
'''

View Code

      data.json

{"count": 2}

View Code

   2、互斥锁与join的区别(用程序代码来比较和解释)

    同样两个文件,分别是:互斥锁与join的区别.py 和 data.json文件。

    互斥锁与join的区别.py

#!/usr/bin/env python3
#-*- coding:utf-8 -*-
# write by congcong
import time
import json
from multiprocessing import Processdef search(name):time.sleep(1)dic = json.load(open('data.json','r',encoding='utf-8'))print('<%s> 剩余票数 [%d]'%(name,dic['count']))def buy(name):time.sleep(1)dic = json.load(open('data.json','r',encoding='utf-8'))if dic['count'] > 0:dic['count'] -= 1json.dump(dic,open('data.json','w',encoding='utf-8'))time.sleep(1)print('<%s> 购票成功!'%name)else:print('<%s> 购票失败!'%name)def task(name):search(name)buy(name)if __name__ == '__main__':for i in range(1,4):p = Process(target=task,args=('顾客%d'%i,))p.start()p.join()
'''
<顾客1> 剩余票数 [2]
<顾客1> 购票成功!
<顾客2> 剩余票数 [1]
<顾客2> 购票成功!
<顾客3> 剩余票数 [0]
<顾客3> 购票失败!
'''
# join 和 互斥锁 都可以将并发执行变为串行执行
'''
区别:join 对应修改的是全局的,而互斥锁则相对更加灵活,可以具体到全局的某一具体功能(共享)'''

View Code

    data.json

{"count":2}

View Code

    注意二者区别:join 对应修改的是全局的,而互斥锁则相对更加灵活,可以具体到全局的某一具体功能(共享)。

  

二、队列

  1、队列的理解

    进程彼此之间互相隔离,要实现进程间通信(IPC),multiprocessing模块支持两种形式:队列和管道,

    这两种方式都是使用消息传递的。

  2、队列的用法

    2.1 创建队列的类(底层就是以管道和锁定的方式实现):    

Queue([maxsize]):创建共享的进程队列,Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递。

    2.2 参数介绍:

maxsize是队列中允许最大项数,省略则无大小限制。
但需要明确:1、队列内存放的是消息而非大数据2、队列占用的是内存空间,因而maxsize即便是无大小限制也受限于内存大小

    2.3 主要方法

q.put 方法用以插入数据到队列中。
q.get 方法可以从队列读取并且删除一个元素。

    2.4 队列实例 

#!/usr/bin/env python3
#-*- coding:utf-8 -*-
# write by congcongfrom multiprocessing import Queueq = Queue(3) # 括号内可指定最大存放数目,当未指定数目时,表示无限,但不建议这样,因为内存是有限的
q.put('hello world') # put()表示存放队列中(队列创建在内存中,可共享)
q.put({'a':1,'b':2})
q.put([1,2,3,4])
print(q.full())  #  True ---> full()判断队列是否已满
#q.put(666)  # 达到指定存放数目时,就会卡住,不再存放
print(q.get()) # hello world  ---> 队列为先进先出的线性表
print(q.get()) # {'a': 1, 'b': 2}
print(q.get()) # [1, 2, 3, 4]
print(q.empty()) # True   ---> empty() 判断队列是否为空
print(q.get()) # 当队列数据都完全取出后,再取不再运行

三、生产者消费者模型

  1、 生产者消费者模型介绍

    1.1 为什么要使用生产者消费者模型?

    生产者指的是生产数据的任务,消费者指的是处理数据的任务,在并发编程中,如果生产者处理速度很快,

  而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的

  处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。

    1.2 什么是生产者和消费者模式?    

    生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间

  不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给

  阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了

  生产者和消费者的处理能力。

    这个阻塞队列就是用来给生产者和消费者解耦的。

  

  2、生产者消费者模型实现 

#!/usr/bin/env python3
#-*- coding:utf-8 -*-
# write by congcong
import time
from multiprocessing import Process,Queue
# 解耦合;平衡了生产者和消费者的速度差
def producer(q):for i in range(1,6):good = '烤鸭%d'%itime.sleep(0.5)print('生产者生产了%s'%good)q.put(good)def consumer(q):while True:good = q.get()if good is None:breaktime.sleep(1)print('消费者吃了%s'%good)if __name__ == '__main__':# 容器q = Queue()# 生产者们p1 = Process(target=producer,args=(q,))p2 = Process(target=producer,args=(q,))p3 = Process(target=producer,args=(q,))# 消费者们c1 = Process(target=consumer,args=(q,))c2 = Process(target=consumer,args=(q,))c1.daemon = True # 守护进程,随主进程一同死去c2.daemon = Truep1.start()p2.start()p3.start()c1.start()c2.start()p1.join()p2.join()p3.join()q.put(None) # 结束信号,位于生产者所有的产品最后,栈尾q.put(None) # 结束信号的个数与消费者数量对应print('来客人了!') # 主进程
'''
1、主进程需要等生产者p1、p2、p3进程结束后运行
2、p1、p2、p3是在消费者把所有数据取完后才会结束
3、等p1、p2、p3结束了,消费者也就没有存在的意义了,应该随主进程一同死掉,故消费者应设成守护进程
'''

View Code

四、利用 JoinableQueue模块改进生产者消费者模型

  1、JoinableQueue(maxsize)的理解

这就像是一个Queue对象,但队列允许项目的使用者通知生成者项目已经被成功处理。通知进程是使用共享的信号和条件变量来实现的。

  2、参数介绍

maxsize是队列中允许最大项数,省略则无大小限制。

  3、方法介绍

JoinableQueue的实例p除了与Queue对象相同的方法之外还具有:
q.task_done():使用者使用此方法发出信号,表示q.get()的返回项目已经被处理。如果调用此方法的次数大于从队列中删除项目的数量,将引发ValueError异常
q.join():生产者调用此方法进行阻塞,直到队列中所有的项目均被处理。阻塞将持续到队列中的每个项目均调用q.task_done()方法为止

  4、改进后的程序 

#!/usr/bin/env python3
#-*- coding:utf-8 -*-
# write by congcong
import time
from multiprocessing import Process,JoinableQueuedef producer(q):for i in range(1,5):good = '烤红薯%s'%itime.sleep(1)print('生产者生产了烤红薯%s'%good)q.put(good)q.join() # 等待队列进程执行完,即队列内数据被取完def consumer(q):while True:good = q.get()if good is None:breaktime.sleep(1)print('消费者吃了%s'%good)q.task_done() # 给生产者发送结束信号,即队列已经有一个数据被取走了if __name__ == '__main__':# 容器q = JoinableQueue()# 生产者p1 = Process(target=producer,args=(q,))p2 = Process(target=producer,args=(q,))p3 = Process(target=producer,args=(q,))# 消费者c1 = Process(target=consumer,args=(q,))c2 = Process(target=consumer,args=(q,))c1.daemon = True # 将消费者进程设为守护进程,以便消费者进程随主进程结束而结束c2.daemon = True# 发送信号
    p1.start()p2.start()p3.start()c1.start()c2.start()p1.join()p2.join()p3.join()print('主进程')

五、生产者消费者模型总结

 5.1 程序中有两类角色   

  一类负责生产数据(生产者)一类负责处理数据(消费者)

 5.2 引入生产者消费者模型为了解决的问题是

平衡生产者与消费者之间的速度差
程序解开耦合

 5.3 如何实现生产者消费者模型 

生产者<--->队列<--->消费者



转载于:https://www.cnblogs.com/schut/p/9017051.html

并发编程之多进程篇之四相关推荐

  1. 并发编程之多线程篇之四

    主要内容: 一.信号量 二.Event事件 三.定时器 四.线程queue 五.进程池与线程池 1️⃣ 信号量 1.信号量的理解 信号量也是一把锁,可以指定信号量为5,对比互斥锁同一时间只能有一个任务 ...

  2. 并发编程之多进程编程(python版)

    目录 1 python多进程编程概述 2 需求和方案 背景: 需求: 解决思路: 需要解决的问题和方案: 3 完整代码 1 python多进程编程概述 python中的多线程无法利用多核优势,如果想要 ...

  3. 并发编程之多进程进程进程

    Python 并发编程之多进程 1.1 multiprocessing 模块 Python 中的多线程无法利用多核资源,如果想要充分的使用多核 cpu 的资源,在 Python 中大部分情况需要使用多 ...

  4. Python 并发编程:PoolExecutor 篇

    个人笔记,如有疏漏,还请指正. 使用多线程(threading)和多进程(multiprocessing)完成常规的并发需求,在启动的时候 start.join 等步骤不能省,复杂的需要还要用 1-2 ...

  5. Java-gt;Android并发编程引气入门篇

    Android的并发编程,即多线程开发,而Android的多线程开发模型也是源于Java中的多线程模型. 所以本篇也会先讲一些Java中的多线程理念,再讲解具体涉及的类,最后深入Android中的并发 ...

  6. Python并发编程之多进程(二)

    十.进程同步 进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终端,是没有问题的, 而共享带来的是竞争,竞争带来的结果就是错乱,如何控制,就是加锁处理 ---------- ...

  7. python互斥锁原理_python并发编程之多进程1------互斥锁与进程间的通信

    一.互斥锁 进程之间数据隔离,但是共享一套文件系统,因而可以通过文件来实现进程直接的通信,但问题是必须自己加锁处理. 注意:加锁的目的是为了保证多个进程修改同一块数据时,同一时间只能有一个修改,即串行 ...

  8. python 多进程并发_python并发编程之多进程

    一 multiprocessing模块介绍 python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),在python中大部分情况需要使用多进程.P ...

  9. 并发编程之——多进程

    一.基本概念 1.1 进程 其实进程就是正在进行的一个程序或者任务,而负责执行任务的是CPU,执行任务的地方是内存.跟程序相比,程序仅仅是一堆代码而已,而程序运行时的过程才是进程.另外同一个程序执行两 ...

最新文章

  1. linux下出现ping:unknown host www.baidu.com问题时的解决办法——ubuntu下局域网络的配置...
  2. nodejs运行python_如何在后台Python中运行子进程命令来启动nodejs服务器
  3. 百度CTO王海峰:百度Paddle已支持超过70个主流的模型
  4. Careercup | Chapter 4
  5. LeetCode 213 House Robber II Python
  6. 利用银行家算法避免死锁(C++实现)
  7. chkconfig命令会立即生效吗_UG绘制波纹管,整体变形命令你会用吗?
  8. python关键字详解_Python 中的关键字with详解
  9. 浮动div,回到顶部
  10. ETL异构数据源Datax_图形化数据同步_11
  11. Python 读取/存储 yaml 文件
  12. 小笔记-简单但够用系列_informix静默安装
  13. 国际品牌会员俱乐部VTN甄选全球好物 把握消费升级趋势 引领品牌高质量发展
  14. 全景图(三):在Unity3D上实现360°球面投影
  15. JAVA求解【乱序整数序列两数之和绝对值最小】
  16. 磨耳朵鼻祖Super Simple Songs下载之《Rain Rain Go Away》
  17. Ruby_01_环境安装
  18. Linux 故障排查-测试网络端口连通性
  19. 迁移学习基础知识(一)——分类及应用
  20. 2022字节跳动数据仓库实习面经

热门文章

  1. Java将视频转为缩略图--ffmpeg
  2. 贪心算法(Greedy Algorithm)最小生成树 克鲁斯卡尔算法(Kruskal#39;s algorithm)
  3. CSS样式:background-position word-wrap是控制换行的。
  4. FreeMarker简介及其语法
  5. C语言函数sscanf:从一个字符串中读进与指定格式相符的数据
  6. ajax点赞只能点一次,php+mysql+ajax局部刷新点赞取消点赞功能(每个账号只点赞一次).pdf...
  7. win7没有个性化如何把计算机放到桌面,win7系统家庭版右键没有个性化设置桌面壁纸...
  8. uefi启动u盘安装系统_技嘉uefi启动怎么安装win8.1系统【安装教程】
  9. code vs 把所有行拼接成一行_关于SQL Server将一列的多行内容拼接成一行的问题讨论...
  10. python目录在哪里_python安装后的目录在哪里