互斥锁与队列进程数据共享

一、互斥锁:

那一个抢票的例子来说:一个文件中放一行数据为票的数量  {"number": 1 } 票数为1

from multiprocessing import Process,Lock
import json
import time

def set(i):
    data_read = json.load(open('info_user.txt', 'r', encoding='utf-8'))  # 先查看票数,所有人都可以一起查看
    time.sleep(2)  # 模拟读数据的网络延迟
    print('%s 查看有%s张票' % (i,data_read['number']))
def take(i):
    data = json.load(open('info_user.txt', 'r', encoding='utf-8'))
    if data['number']>0:
        data['number'] = data['number']-1
        time.sleep(2)   # 模拟写数据的网络延迟
        json.dump(data,open('info_user.txt','w',encoding='utf-8'))
        print('%s 购票成功 ' % i)

def run(i):
    set(i)
    take(i)



if __name__ == '__main__':

    for i in range(10):
        p = Process(target=run,args=(i,))
        p.start()


结果:会发现所有的人都买到票了,但是票只有一张,多进程效率虽然高但是数据混乱不安全,所以这个互斥锁就是解决数据混乱不安全这个问题的

有1张票
有1张票
有1张票
有1张票
有1张票
有1张票
有1张票
有1张票
有1张票
有1张票
0 购票成功 
1 购票成功 
2 购票成功 
3 购票成功 
4 购票成功 
5 购票成功 
6 购票成功 
7 购票成功 
8 购票成功 
9 购票成功 

解决: 这里加上了互斥锁,舍弃效率保证了数据的安全性

from multiprocessing import Process,Lock
import json
import time

def set(i):
    data_read = json.load(open('info_user.txt', 'r', encoding='utf-8'))  # 先查看票数,所有人都可以一起查看
    time.sleep(2)  # 模拟读数据的网络延迟
    print('%s 查看有%s张票' % (i,data_read['number']))
def take(i):

    data = json.load(open('info_user.txt', 'r', encoding='utf-8'))
    if data['number']>0:
        data['number'] = data['number']-1
        time.sleep(2)   # 模拟写数据的网络延迟
        json.dump(data,open('info_user.txt','w',encoding='utf-8'))
        print('%s 购票成功 ' % i)

def run(i,lock):
    set(i)
    lock.acquire()     # 这里也可以用with lock:   相当于lock.acquire(),执行完自代码块自动执行lock.release()
    take(i)
    lock.release()



if __name__ == '__main__':
    lock = Lock()
    for i in range(10):
        p = Process(target=run,args=(i,lock))
        p.start()

结果:

0 查看有1张票
2 查看有1张票
3 查看有1张票
1 查看有1张票
4 查看有1张票
6 查看有1张票
5 查看有1张票
8 查看有1张票
7 查看有1张票
9 查看有1张票
0 购票成功 

二、队列与管道

队列与管道:队列和管道都是将数据存放于内存中,而队列又是基于(管道+锁)实现的,可以让我们从复杂的锁问题中解脱出来,因而队列才是进程间通信的最佳选择

obj = Queue(max_size)  定义方式,max_size是限制队列中的个数

但是:队列中的占用的是内存空间,max_size是受内存大小限制的

  队列中放的数据不能是大型的数据

# q.put方法用以插入数据到队列中。
# q.get方法可以从队列读取并且删除一个元素。
from multiprocessing import Queue
q = Queue(3)

q.put('1234')
q.put([1,2,3,'e'])
q.put(1)
print(q.full())  # 查看该队列是否已经满了

print(q.get())
print(q.get())
print(q.get())
print(q.empty())  # 是否已经为空

# print(q.get())  # 拿完后队列里没有值了,就死在这里了,阻塞了

 三、队列中的生产者与消费者模型

生产者与消费者模型我是这样理解的,生产者只顾生产,消费者只顾消费,两个是分开进行的,绝不是生产者生产一个消费者就消费一个,生产者还要等消费者消费完再生产一个

1、队列中的堵塞

from multiprocessing import Process,Queue
import time


def producer(q):
    for i in range(10):
        time.sleep(0.5)
        ret = '包子 %s' % i
        print('生产者生产了 %s' % ret )
        q.put(ret)    # 生产一个就往队列里面放一个


def consumer(q):
    while 1:
        ret = q.get()   # 当队列中的值取完后再取就会进行阻塞,一直阻塞到有人往里面放值了,代码再往下走
        if ret==None:break
        time.sleep(1)
        print('消费者吃了 %s' % ret)



if __name__ == '__main__':
    q = Queue()
    p1 = Process(target=producer,args=(q,))

    c1 = Process(target=consumer,args=(q,))

    p1.start()
    c1.start()

    print('')

队列阻塞解决:在生产者生产结束的时候给消费者发送一个消息,说我生产完了没有了

from multiprocessing import Process,Queue
import time


def producer(q):
    for i in range(10):
        time.sleep(0.5)
        ret = '包子 %s' % i
        print('生产者生产了 %s' % ret )
        q.put(ret)    # 生产一个就往队列里面放一个


def consumer(q):
    while 1:
        ret = q.get()   # 取一个出来
        if ret==None:break
        time.sleep(1)
        print('消费者吃了 %s' % ret)



if __name__ == '__main__':
    q = Queue()
    p1 = Process(target=producer,args=(q,))

    c1 = Process(target=consumer,args=(q,))

    p1.start()
    c1.start()


    p1.join()   # 在确保生产者进程结束了

    q.put(None)  # 发送None,但是如果有多个生产者那每个都要进行发送,感觉很low,还有一种队列解决方法

    print('')
第一种
from multiprocessing import Process,JoinableQueue
import time


def producer(q):
    for i in range(10):
        time.sleep(0.5)
        ret = '包子 %s' % i
        print('生产者生产了 %s' % ret )
        q.put(ret)
    q.join()   # 生产者调用此方法进行阻塞,直到队列中所有的项目均被处理。阻塞将持续到队列中的每个项目均调用q.task_done()方法为止


def consumer(q):
    while 1:
        ret = q.get()
        time.sleep(1)
        print('消费者吃了 %s' % ret)
        q.task_done()  # q.task_done():使用者使用此方法发出信号,表示q.get()的返回项目已经被处理。如果调用此方法的次数大于从队列中删除项目的数量,将引发ValueError异常



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()
    # 1、主进程等生产者p1、p2、p3结束
    # 2、而p1、p2、p3是在消费者把所有数据都取干净之后才会结束
    # 3、所以一旦p1、p2、p3结束了,证明消费者也没必要存在了,应该随着主进程一块死掉,因而需要将生产者们设置成守护进程
    print('')
第二种,JoinQueue模块

 

posted @ 2019-09-04 21:47  tiwe  阅读(338)  评论(0编辑  收藏  举报