Fork me on GitHub

并发编程:互斥锁,生产者,消费者模型

一 守护进程:

守护进程也就是一个进程会随着另一个进程(被守护进程)的结束而结束:

from multiprocessing import Process
import time
def func(name):
    time.sleep(2)
    print('%s is running'%name)
if __name__ == '__main__':
    # t=Process(target=func,args=('feng',))
    t=Process(target=func,kwargs={'name':'feng'})
    t.daemon=True#我们通过把子进程变成守护进程。
    t.start()
    print('zhu'.center(50,'*'))
输出结果:***********************zhu************************
从结果我们可以看出,主进程一结束,子进程就结束。

二 互斥锁:保证每一个我们的子进程完整的运行完之后才会运行下一个子进程,但是在当前子进程阻塞的时候可以去进行新的子进程的创建。

from multiprocessing import Process,Lock
import time
import random
mutex=Lock()
def task1(lock):
    lock.acquire()#不能连续的acquire,必须抢一次,释放一次才能继续抢。
    print('task1:name1')
    time.sleep(random.randint(1,2))
    print('task1:age1')
    time.sleep(random.randint(1, 2))
    print('task1:sex1')
    time.sleep(random.randint(1, 2))
    lock.release()
def task2(lock):
    lock.acquire()
    print('task1:name2')
    time.sleep(random.randint(1, 2))
    print('task1:age2')
    time.sleep(random.randint(1, 2))
    print('task1:sex2')
    time.sleep(random.randint(1, 2))
    lock.release()
def task3(lock):
    lock.acquire()
    print('task1:name3')
    time.sleep(random.randint(1, 2))
    print('task1:age3')
    time.sleep(random.randint(1, 2))
    print('task1:sex3')
    time.sleep(random.randint(1, 2))
    lock.release()
if __name__ == '__main__':
    p1=Process(target=task1,args=(mutex,))
    p2=Process(target=task2,args=(mutex,))
    p3=Process(target=task3,args=(mutex,))
    p1.start()
    p2.start()
    p3.start()
我们从multiprocessing模块导入Lock,通过Process实例化出三个对象p1,p2,p3,这三个对象在执行的时候就是三个进程,互斥锁会保证三个进程之间公平竞争cpu资源
join与互斥锁的区别:二者原理一样,把并发变成串行,保证有序,join人为的指定顺序
应该谁先启动谁先来用,如果使用join那么我们的顺序就固定了,必须p1先开始

而互斥锁是所有的进程平等的争抢,谁先抢到谁先用。抢到之后运行完了一定要要释放,要不然别的进程没办法拿到锁。
我们使用join同样可以实现一个串行的效果:
from multiprocessing import Process
import time
import random
def task1():
    print('task1:name1')
    time.sleep(random.randint(1,2))
    print('task1:age1')
    time.sleep(random.randint(1, 2))
    print('task1:sex1')
    time.sleep(random.randint(1, 2))
def task2():
    print('task1:name2')
    time.sleep(random.randint(1, 2))
    print('task1:age2')
    time.sleep(random.randint(1, 2))
    print('task1:sex2')
    time.sleep(random.randint(1, 2))

def task3():
    print('task1:name3')
    time.sleep(random.randint(1, 2))
    print('task1:age3')
    time.sleep(random.randint(1, 2))
    print('task1:sex3')
    time.sleep(random.randint(1, 2))
if __name__ == '__main__':
    p1=Process(target=task1,)
    p2=Process(target=task2,)
    p3=Process(target=task3,)
    p1.start()
    p1.join()
    p2.start()
    p2.join()
    p3.start()
    p3.join()
使用join实现有序输出

三 基于互斥锁模拟抢票系统:

互斥锁可以有选择的让一部分代码(修改共享数据的代码)串行。而join则让所有的代码都按照一个确定的顺序运行。

#文件db的内容为:{"count":1}
#注意一定要用双引号,不然json无法识别
from multiprocessing import Process,Lock
import time,json,random
def search():
    dic=json.load(open('db.txt'))
    print('\033[43m剩余票数%s\033[0m' %dic['count'])

def get():
    dic=json.load(open('db.txt'))
    time.sleep(0.1) #模拟读数据的网络延迟
    if dic['count'] >0:
        dic['count']-=1
        time.sleep(0.2) #模拟写数据的网络延迟
        json.dump(dic,open('db.txt','w'))
        print('\033[43m购票成功\033[0m')

def task(lock):
    search()
    lock.acquire()
    get()
    lock.release()
if __name__ == '__main__':
    lock=Lock()
    for i in range(100): #模拟并发100个客户端抢票
        p=Process(target=task,args=(lock,))
        p.start()

同样的我们也可以使用join实现一个抢票系统,但是这个时候,每次只能一个人来操作,也就是说,每次只能提供一个人进行查票或则买票,其他人只有等到前一个用户退出之后
才能进行查票或则购票,而互斥锁可以提供多个用户同时查票,单个用户购票。

#文件db的内容为:{"count":1}
#注意一定要用双引号,不然json无法识别
from multiprocessing import Process,Lock
import time,json,random
def search():
    dic=json.load(open('db.txt'))
    print('\033[43m剩余票数%s\033[0m' %dic['count'])

def get():
    dic=json.load(open('db.txt'))
    time.sleep(0.1) #模拟读数据的网络延迟
    if dic['count'] >0:
        dic['count']-=1
        time.sleep(0.2) #模拟写数据的网络延迟
        json.dump(dic,open('db.txt','w'))
        print('\033[43m购票成功\033[0m')

def task(lock):
    search()
    get()
if __name__ == '__main__':
    lock=Lock()
    for i in range(100): #模拟并发100个客户端抢票
        p=Process(target=task,args=(lock,))
        p.start()
使用join实现每次单个用户购票

总结:其实基于互斥锁和join这两种方式效率都是比较低的,但是互斥锁要比join效率更高,而且互斥锁可以实现CPU利用更大化,同时开启多个进程,我们可以让多个进程都查看余票数,但是这个余票数是不可靠的,因为多个进程都执行到这一步,但是最终只有一个进程能拿到这个票,也就是说大家所看到的其实是一张票。而如果我们使用join模式的话,那么只要我们能查看到票就能买到票。

四 ICP机制:我们又称他为基于消息的机制。

进程彼此之间互相隔离,要实现进程间通信(IPC),multiprocessing模块支持两种形式:队列和管道,这两种方式都是使用消息传递的

进程之间的通信:进程之间是相互独立的,所以进程之间的通讯需要介质:管道和队列,我们优先使用队列。

队列的通讯是一个有序的过程,先进先出,不会导致我们的信息混乱,而管道的两端都可以进行存取,所以容易造成错乱。

进程之间的通信:
必须找到一种介质:
介质需要满足的条件是: 1 是所有进程共享的 2 必须是内存空间 3 这种机制需要帮我们自动处理锁的问题

列队:队列的一些用法,其实底层就是互斥锁+管道的原理。

 队列:先进先出,里面不应该存放数据量较大的数据,又来存方进程之间沟通的消息,数据量不应该过大。
 1 共享的空间
 2 是内存空间
 3 自动处理锁的问题
from multiprocessing import Queue
q
=Queue(3),最多只能put三次,这里的3受限于内存空间的大小,也就是说,maxsize是可以无限大的,但是那样没意义。 q.put()#括号里面obj,在Python中一切皆为对象。 q.put('first',block=False) q.put() q.get() q.get() q.get('first',block=True,timeout=3)#当block为True时,管道中没有东西给你来取的时候,就会一直等待,程序不会终止。 q.get('first',block=False,timeout=3)#这个时候如果管道中没有东西取,等待三秒之后就会抛出异常,我们可以使用try来处理此异常。 q.put()#括号里面obj q.put('first',block=True,timeout=3)#等3秒 q.put('first',block=True,timeout=3)其中block说的是阻塞与非阻塞的问题,而timeout只有在非阻塞的情况下运用,规定等待一段时间之后抛出异常, q.put()

 不得不感慨multiprocessing真的是一个强大的模块。。。。。。。。

五 生产者,消费者模型:

生产者消费者模型主要分为:生产者与消费者两大类

生产者:把负责造数据的任务称为生产则,

消费者:接收生产者造出的数据来作进一步处理。

生产者消费者模型三要素:1 生产者, 2 消费者 , 3 队列

使用场景:一定是我们的程序中出现明显的两类任务,一类人物是负责生产而另一类任务是负责处理生产的数据的



好处:1 实现了生产者与消费者解耦合

          2 平衡了生产力与消费力,生产者可以一直不停的生产,消费者可以一直不停的处理,因为二者不再直接沟通。而是直接从阻塞队列里取,
阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。

 












 

posted @ 2018-04-25 14:45  道阻切长  阅读(732)  评论(1编辑  收藏  举报