互斥锁和进程之间的通信

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

大家好,又见面了,我是你们的朋友全栈君。

互斥锁

进程之间数据隔离,但是共享一套文件系统,因而可以通过文件来实现进程直接的通信,但问题是必须自己加锁处理。

注意:加锁的目的是为了保证多个进程修改同一块数据时,同一时间只能有一个修改,即串行的修改,没错,速度是慢了,牺牲了速度而保证了数据安全。

示例一.上厕所

你上厕所的时候肯定得锁门吧,有人来了看见门锁着,就会在外面等着,等你吧门开开出来的时候,下一个人才去上厕所。

 1 from multiprocessing import Process,Lock
 2 import os
 3 import time
 4 def work(mutex):
 5     mutex.acquire()
 6     print('task[%s] 上厕所'%os.getpid())
 7     time.sleep(3)
 8     print('task[%s] 上完厕所'%os.getpid())
 9     mutex.release()
10 if __name__ == '__main__':
11     mutex = Lock()
12     p1 = Process(target=work,args=(mutex,))
13     p2 = Process(target=work,args=(mutex,))
14     p3 = Process(target=work,args=(mutex,))
15     p1.start()
16     p2.start()
17     p3.start()
18     p1.join()
19     p2.join()
20     p3.join()
21     print('主')

示例二、模拟抢票(也是利用了互斥锁的原理  :LOCK互斥锁)

 1 import json
 2 import time
 3 import random
 4 import os
 5 from multiprocessing import Process,Lock
 6 def chakan():
 7     dic = json.load(open('piao',))  # 先查看票数,也就是打开那个文件
 8     print('剩余票数:%s' % dic['count'])  # 查看剩余的票数
 9 def buy():
10     dic = json.load(open('piao',))
11     if dic['count']>0: #如果还有票
12         dic['count']-=1 #就修改里面的值-1
13         time.sleep(random.randint(1,3)) #执行里面买票的一系列操作就先不执行了,让睡一会代替(并且随机的睡)
14         json.dump(dic,open('piao','w'))
15         print('%s 购票成功' % os.getpid())  # 当前的那个id购票成功
16 def task(mutex): #抢票
17     chakan()  #因为查看的时候大家都可以看到,不需要加锁
18     mutex.acquire() #加锁
19     buy() #买的时候必须一个一个的买,先等一个人买完了,后面的人在买
20     mutex.release() #取消锁
21 if __name__ == '__main__':
22     mutex = Lock()
23     for i in range(50):#让50个人去访问那个票数
24         p = Process(target=task,args=(mutex,))
25         p.start()

Process对象的其他属性

p.daemon :守护进程(必须在开启之前设置守护进程):如果父进程死,子进程p也死了

p.join:父进程等p执行完了才运行主进程,是父进程阻塞在原地,而p仍然在后台运行。

terminate:强制关闭。(确保p里面没有其他子进程的时候关闭,如果里面有子进程,你去用这个方法强制关闭了就会产生僵尸进程(打个比方:如果你老子挂了,你还没挂,那么就没人给你收尸了,啊哈哈))

is_alive:关闭进程的时候,不会立即关闭,所以is_alive立刻查看的结果可能还是存活

p.join():父进程在等p的结束,是父进程阻塞在原地,而p仍然在后台运行

p.name:查看名字

p.pid :查看id

我们可以简单介绍一下僵尸进程:

子进程运行完成,但是父进程迟迟没有进行回收,此时子进程实际上并没有退出,其仍然占用着系统资源,这样的⼦进程称为僵尸进程

因为僵尸进程的资源一直未被回收,造成了系统资源的浪费,过多的僵尸进程将造成系统性能下降,所以应避免出现僵⼫进程。

 1 from multiprocessing import Process
 2 import os
 3 import time
 4 def work():
 5     print('%s is working'%os.getpid())
 6     time.sleep(3)
 7 if __name__ == '__main__':
 8     p1 =Process(target=work)
 9     p2 =Process(target=work)
10     p3 =Process(target=work)
11     # p1.daemon = True
12     # p2.daemon = True #守护进程(守护他爹)
13     # p3.daemon = True  #主进程死了子进程也死了(就不会执行子进程了)
14     p1.start()
15     p2.start()
16     p3.start()
17 
18     p3.join()
19     p2.join()
20     p1.join() #多个join就是在等花费时间最长的那个运行完就执行主程序了
21     print('主程序')
22 
23 # -了解方法---------------
24 #     p1.terminate()  #强制关闭进程
25 #     time.sleep(3)
26 #     print(p1.is_alive())  #看是不是还活着
27 #     print(p1.name) #查看进程名字
28 #     print(p1.pid) #查看id号
29 #     print('主程序')

进程间的三种通信(IPC)方式

方式一:队列(推荐使用)

  进程彼此之间互相隔离,要实现进程间通信(IPC),multiprocessing模块支持两种形式:队列和管道,这两种方式都是使用消息传递的
1.队列:队列类似于一条管道,元素先进先出
需要注意的一点是:队列都是在内存中操作,进程退出,队列清空,另外,队列也是一个阻塞的形态
2.队列分类
队列有很多种,但都依赖与模块queue
queue.Queue() #先进先出
queue.LifoQueue() #后进先出
queue.PriorityQueue() #优先级队列
queue.deque() #双线队列

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

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

参数介绍:

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

方法介绍

q.put方法用以插入数据到队列中,put方法还有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,该方法会阻塞timeout指定的时间,直到该队列有剩余的空间。如果超时,会抛出Queue.Full异常。如果blocked为False,但该Queue已满,会立即抛出Queue.Full异常。
q.get方法可以从队列读取并且删除一个元素。同样,get方法有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,那么在等待时间内没有取到任何元素,会抛出Queue.Empty异常。如果blocked为False,有两种情况存在,如果Queue有一个值可用,则立即返回该值,否则,如果队列为空,则立即抛出Queue.Empty异常.
  
q.get_nowait():同q.get(False)
q.put_nowait():同q.put(False)
 
q.empty():调用此方法时q为空则返回True,该结果不可靠,比如在返回True的过程中,如果队列中又加入了项目。
q.full():调用此方法时q已满则返回True,该结果不可靠,比如在返回True的过程中,如果队列中的项目被取走。
q.qsize():返回队列中目前项目的正确数量,结果也不可靠,理由同q.empty()和q.full()一样

应用:

<span role="heading" aria-level="2">互斥锁和进程之间的通信
<span role="heading" aria-level="2">互斥锁和进程之间的通信

1 # 1.可以往队列里放任意类型的
 2 # 2.先进先出
 3 from multiprocessing import Process,Queue
 4 q= Queue(3)
 5 q.put('first')  #默认block=True
 6 q.put('second')
 7 q.put('third')
 8 
 9 print(q.get())
10 print(q.get())
11 print(q.get())

队列

生产者和消费者模型

在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度

为什么要使用生产者和消费者模式

在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。

什么是生产者消费者模式

生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。

基于队列实现生产者消费者模型

一个生产者和一个消费者(有两种方式)

1、q.put(None):生产者给放一个None进去

<span role="heading" aria-level="2">互斥锁和进程之间的通信
<span role="heading" aria-level="2">互斥锁和进程之间的通信

1 from multiprocessing import Process,Queue
 2 import os
 3 import time
 4 import random
 5 #首先得有生产者和消费者
 6 # 生产者制造包子
 7 '''这种用 q.put(None)放进去一个None的方法虽然解决了问题
 8 但是如果有多个生产者多个消费者,或许框里面没有包子了但是
 9 还有其他的食物呢,你就已经显示空着,这样也可以解决,就是不完美,
10 还可以用到JoinableQueue去解决'''
11 def producter(q):
12     for i in range(10):
13         time.sleep(2) #生产包子得有个过程,就先让睡一会
14         res = '包子%s'%i #生产了这么多的包子
15         q.put(res)  #吧生产出来的包子放进框里面去
16         print('3[44m%s制造了%s3[0m'%(os.getpid(),res))
17     q.put(None) #只有生产者才知道什么时候就生产完了(放一个None进去说明此时已经生产完了)
18 # 消费者吃包子
19 def consumer(q):
20     while True:#假如消费者不断的吃
21         res = q.get()
22         if res is None:break #如果吃的时候框里面已经空了,就直接break了
23         time.sleep(random.randint(1,3))
24         print('3[41m%s吃了%s3[0m' % (os.getpid(),res))
25 if __name__ == '__main__':
26     q = Queue()
27     p1 = Process(target=producter,args=(q,))
28     p2 = Process(target=consumer,args=(q,))
29     p1.start()
30     p2.start()
31     p1.join()
32     p2.join()  #等待执行完上面的进程,在去执行主
33     print('')

方式一

2、利用JoinableQueue

<span role="heading" aria-level="2">互斥锁和进程之间的通信
<span role="heading" aria-level="2">互斥锁和进程之间的通信

1 from multiprocessing import Process,JoinableQueue
 2 import os
 3 import time
 4 import random
 5 #首先得有生产者和消费者
 6 # 消费者吃包子
 7 def consumer(q):
 8     '''消费者'''
 9     while True:#假如消费者不断的吃
10         res = q.get()
11         time.sleep(random.randint(1,3))
12         print('3[41m%s吃了%s3[0m' % (os.getpid(),res))
13         q.task_done() #任务结束了(消费者告诉生产者,我已经吧东西取走了)
14 # 生产者制造包子
15 def producter(q):
16     '''生产者'''
17     for i in range(5):
18         time.sleep(2) #生产包子得有个过程,就先让睡一会
19         res = '包子%s'%i #生产了这么多的包子
20         q.put(res)  #吧生产出来的包子放进框里面去
21         print('3[44m%s制造了%s3[0m'%(os.getpid(),res))
22     q.join()
23 
24 if __name__ == '__main__':
25     q = JoinableQueue()
26     p1 = Process(target=producter,args=(q,))
27     p2 = Process(target=consumer,args=(q,))
28     p2.daemon = True #在启动之前吧消费者设置成守护进程,p1结束了p2也就结束了
29     p1.start()
30     p2.start()
31     p1.join() #在等生产者结束(生产者结束后,就不制造包子了,那消费者一直在吃,就卡住了
32     #都不生产了还吃啥,就把消费者也结束了  )
33       #等待执行完上面的进程,在去执行主
34     print('')

方式二

多个生产者和多个消费者(有两种方式)

 1、q.put(None):生产者给放一个None进去

 2、利用JoinableQueue

<span role="heading" aria-level="2">互斥锁和进程之间的通信
<span role="heading" aria-level="2">互斥锁和进程之间的通信

1 from multiprocessing import Process,JoinableQueue 2 import os 3 import time 4 import random 5 #首先得有生产者和消费者 6 # 消费者吃包子 7 def consumer(q): 8 while True: 9 res = q.get() 10 time.sleep(random.randint(1,3)) 11 print('\033[41m%s吃了%s\033[0m' % (os.getpid(),res)) 12 q.task_done() #任务结束了(消费者告诉生产者,我已经吧东西取走了) 13 def product_baozi(q): 14 for i in range(5): 15 time.sleep(2) 16 res = '包子%s' % i 17 q.put(res) 18 print('\033[44m%s制造了%s\033[0m' % (os.getpid(), res)) 19 q.join() #不用put(None) 了,在等q被取完。(如果数据没有被取完,生产者就不会结束掉) 20 def product_gutou(q): 21 for i in range(5): 22 time.sleep(2) 23 res = '骨头%s' % i 24 q.put(res) 25 print('\033[44m%s制造了%s\033[0m' % (os.getpid(), res)) 26 q.join() 27 def product_doujiang(q): 28 for i in range(5): 29 time.sleep(2) 30 res = '豆浆%s' % i 31 q.put(res) 32 print('\033[44m%s制造了%s\033[0m' % (os.getpid(), res)) 33 q.join() 34 35 if __name__ == '__main__': 36 q = JoinableQueue() 37 # 生产者们:厨师们 38 p1 = Process(target=product_baozi,args=(q,)) 39 p2 = Process(target=product_doujiang,args=(q,)) 40 p3 = Process(target=product_gutou,args=(q,)) 41 42 #消费者们:吃货们 43 p4 = Process(target=consumer,args=(q,)) 44 p5 = Process(target=consumer,args=(q,)) 45 p4.daemon = True 46 p5.daemon = True 47 # p1.start() 48 # p2.start() 49 # p3.start() 50 # p4.start() 51 # p5.start() 52 li = [p1,p2,p3,p4,p5] 53 for i in li: 54 i.start() 55 p1.join() 56 p2.join() 57 p3.join() 58 print('')

View Code

方式二:管道(不推荐使用,了解即可)

管道相当于队列,但是管道不自动加锁

方式三:共享数据(不推荐使用,了解即可)

共享数据也没有自动加锁的功能,所以还是推荐用队列的。感兴趣的可以研究研究管道和共享数据

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/124522.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • 交换机基础配置教程[通俗易懂]

    交换机基础配置教程[通俗易懂]一、带外管理网络的管理控制信息与用户网络的承载业务信息在不同的逻辑信道传送,也就是设备提供专门用于管理的宽带console口:专门用来管理的,并不传输数据,接入一根console线,是一个扁平电缆,另外一端是一个串行接口,用来接入电脑或笔记本上,近端管理时我们会使用console线进行近端管理,设备要做密码恢复时,时必须要进行近端管理的,只有通过console空才能进行密码恢复。以太网口:以太网口用来传输数据管理信息和数据传输是隔离的,所以我们称之为带外管理初次配置:通过console口进行配置,需

  • linux open函数详解

    linux open函数详解原文地址:https://blog.csdn.net/archyli/article/details/78937937一、open函数用来干什么open函数在Linux下一般用来打开或者创建一个文件,我们可以根据参数来定制我们需要的文件的属性和用户权限等各种参数。二、open函数的定义和参数我们首先来看下open函数在Linux下的定义#include&lt;sys/types.h&gt;#i…

  • LinkedHashSet类「建议收藏」

    LinkedHashSet类「建议收藏」概述:LinkedHashSet:元素唯一,元素无索引,元素存取有序由哈希表结构保证元素唯一,由链表保证元素存取有序案例:publicclassDemo1{publicstaticvoidmain(String[]args){//1.创建一个LinkedHashSet集合,指定集合中元素的类型为String类型LinkedHashSet&l…

    2022年10月12日
  • 【工具教程】Dreamweaver教程「建议收藏」

    【工具教程】Dreamweaver教程「建议收藏」1.Dreamweaver代码不自动提示的问题Dreamweaver代码不自动提示的问题,不论是HTML还是CSS,在网上搜索了半天,大部分是Ctrl+Space的方法,也就是说Dreamweaver的代码自动提示快捷键和输入法切换相冲突,按他们的方法,我的根本解决不了。后来终于找到了解决方法:打开Dreamweaver的“编辑”》“首选参数”(快捷键Ctrl+U)》“常规”》右边的“编

  • java获取当前时间到毫秒_java获取当前时间毫秒

    java获取当前时间到毫秒_java获取当前时间毫秒()为获取当前系统时间,也可使用当前时间戳获取时间戳三种方法执行效率比较:importjava.util.Calendar;importjava.util.Date;publicclassTimeTest{……java获得系统时间转换成字符串关键字:java有时候经常用到JAVA时间转换如字符串转换成时间,时间转换成字符串1.long字符串转换成…

  • centos7安装vim命令(vim命令怎么退出)

    那么如何安裝vim呢?输入rpm-qa|grepvim命令,如果vim已经正确安裝,会返回下面的三行代码:root@server1[~]#rpm-qa|grepvimvim-enhanced-7.0.109-7.el5vim-minimal-7.0.109-7.el5vim-common-7.0.109-7.el5

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号