1、进程互斥锁
from multiprocessing import Process, Lock
import json
import time
import random
def search():
with open('ticket.txt', mode='rt', encoding='utf-8') as fp:
dic = json.load(fp)
print('当前剩余票数是{}'.format(dic.get('ticket')))
def buy(n):
with open('ticket.txt', mode='rt', encoding='utf-8') as fp:
dic = json.load(fp)
print('当前剩余票数是{}'.format(dic.get('ticket')))
time.sleep(random.randint(1, 3))
if dic.get('ticket') > 0:
dic['ticket'] -= 1
with open('ticket.txt', mode='wt', encoding='utf-8') as fp:
json.dump(dic, fp)
print('恭喜用户{}买票成功'.format(n))
else:
print('用户{}买票失败,余票不足'.format(n))
def task(mutex, i):
search()
mutex.acquire()
buy(i)
mutex.release()
search()
with mutex:
buy(i)
if __name__ == '__main__':
mutex = Lock()
for i in range(1, 11):
t = Process(target=task, args=(mutex, i))
t.start()
ticket.txt文件内容:
{"ticket": 0}
2、队列
q = Queue(3)
q.put('allen')
q.put(18, timeout=1)
q.put_nowait('male')
q.put('school')
print(q.get())
print(q.get(timeout=1))
print(q.get_nowait())
print(q.get())
print(q.full())
print(q.empty())
3、IPC机制(进程间通信)
Inter-Process Communication: 进程间通信
from multiprocessing import Process, Queue
import os
def task1(q):
print('我是task1进程,我的进程id是: {}'.format(os.getpid()))
q.put('allen')
q.put(18)
def task2(q):
print('我是task2进程,我的进程id是: {}'.format(os.getpid()))
print('task2在队列中取到的数据是: {}'.format(q.get()))
if __name__ == '__main__':
q = Queue(2)
t1 = Process(target=task1, args=(q,))
t1.start()
t2 = Process(target=task2, args=(q,))
t2.start()
print('主进程在队列中取到的数据是: {}'.format(q.get()))
4、生产者和消费者模型
4.1、定义
在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度
4.2、为什么要使用生产者和消费者模式
在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式
4.3、什么是生产者消费者模式
1、生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题
2、生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取
3、阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/fdb4fd25e662e02b6dc3ce86d0cec2bc.png)
4.4、基于队列实现生产者消费者模型
4.4.1、multiprocessing - Queue 实现-简易版本
from multiprocessing import Process, Queue
import time, random
def producer(name, food, q):
for i in range(4):
time.sleep(random.randint(1, 3))
f = '{}生产的{}{}'.format(name, food, i)
print(f)
q.put(f)
time.sleep(random.randint(1, 3))
def consumer(q, name):
while True:
food = q.get()
if food is None: break
f = '{}消费了{}'.format(name, food)
print(f)
time.sleep(random.randint(1, 3))
if __name__ == '__main__':
q = Queue()
p = Process(target=producer, args=('张三', '包子', q))
p.start()
p1 = Process(target=producer, args=('李四', '面条', q))
p1.start()
c = Process(target=consumer, args=(q, '王五'))
c.start()
c1 = Process(target=consumer, args=(q, '张六'))
c1.start()
p.join()
p1.join()
q.put(None)
q.put(None)
总结:
使用Queue组件实现的缺点就是,实现了多少个消费者consumer进程,就需要在最后往队列中添加多少个None标识,方便生产完毕结束消费者consumer进程。否则,p.get() 不到任务会阻塞子进程,因为while循环,直到队列q中有新的任务加进来,才会再次执行。而我们的生产者只能生产这么多东西,所以相当于程序卡死
4.4.2、multiprocessing - JoinableQueue 实现-终极版本
from multiprocessing import JoinableQueue, Process
import time, random
def producer(name, food, q):
for i in range(4):
time.sleep(random.randint(1, 3))
f = '{}生产的{}{}'.format(name, food, i)
q.put(f)
print(f)
q.join()
def consumer(name, q):
while True:
food = q.get()
print('{}消费了{}'.format(name, food))
time.sleep(random.randint(1, 3))
q.task_done()
if __name__ == '__main__':
q = JoinableQueue()
p1 = Process(target=producer, args=('张三', '包子', q))
p1.start()
p2 = Process(target=producer, args=('李四', '面条', q))
p2.start()
c1 = Process(target=consumer, args=('王五', q))
c1.daemon = True
c1.start()
c2 = Process(target=consumer, args=('张六', q))
c2.daemon = True
c2.start()
p1.join()
p2.join()
总结:
使用JoinableQueue组件,是因为JoinableQueue中有两个方法: task_done()和join() 。首先说join()和Process中的join()的效果类似,都是阻塞当前进程,防止当前进程结束。但是JoinableQueue的join()是和task_down()配合使用的。
Process中的join()是等到子进程中的代码执行完毕,就会执行主进程join()下面的代码。而JoinableQueue中的join()是等到队列中的任务数量为0的时候才会执行q.join()下面的代码,否则会一直阻塞。
task_down()方法是每获取一次队列中的任务,就需要执行一次。直到队列中的任务数为0的时候,就会执行JoinableQueue的join()后面的方法了。所以生产者生产完所有的数据后,会一直阻塞着。不让p1和p2进程结束。等到消费者get()一次数据,就会执行一次task_down()方法,从而队列中的任务数量减1,当数量为0后,执行JoinableQueue的join()后面代码,从而p1和p2进程结束。
因为p1和p2添加了join()方法,所以当子进程中的consumer方法执行完后,才会往下执行。从而主进程结束。因为这里把消费者进程c1和c2 设置成了守护进程,主进程结束的同时,c1和c2 进程也会随之结束,进程都结束了。所以消费者consumer方法也会结束