Lock, Event, Queue, JoinableQueue, Semaphore, Mananger

1. 锁: 同一时间允许一个进程上一把锁 就是Lock

    加锁可以保证多个进程修改同一块数据时,同一时间只能有一个任务可以进行修改,即串行的修改,速度会变得稍慢,但牺牲速度却保证了数据安全。

    互斥锁Lock : 互斥锁就是进程的互相排斥,谁先抢到资源,谁就上锁改资源内容,为了保证数据的同步性

    注意:两个或以上的锁一起上,不开锁,会造成死锁.上锁和解锁是一对.

1.1  lock的基本用法:

from multiprocessing import Process,Lock
# 创建一把锁:
lock = Lock()
# 上锁:
lock.acquire()
# lock.acquire() 如果上多个锁不解锁,就会变成死锁
# 解锁:
lock.release()

1.2 模拟12306抢票

# 模拟12306抢票软件
from multiprocessing import Process,Lock
import json,time
lock = Lock()
# 读取,更新票数
def wr_ticket(sign,dic=None):
    if sign == "r":
        with open("ticket.txt",mode="r",encoding="utf-8") as fp:
            dic = json.load(fp)
            return dic
    elif sign == "w":
        with open("ticket.txt",mode="w",encoding="utf-8") as fp:
            json.dump(dic,fp)

# 抢票方法
def get_ticket(person):
    dic = wr_ticket("r")
    if dic["count"] > 0:
        print("%s可以回家了"%(person))
        dic["count"] -= 1
        wr_ticket("w",dic=dic)
    else:
        print("%s回不了家了!"%(person))

def run(person,lock):
    dic = wr_ticket("r")
    print("%s在查询余票,余票%d张"%(person,dic["count"]))

    lock.acquire()
    get_ticket(person)  # 加锁让进程从异步变成了同步
    lock.release()

if __name__ == "__main__":
    lst = ["bob","alice","jack","rose"]
    for i in lst:
        p = Process(target=run,args=(i,lock))
        p.start()

1.3  信号量Semaphore: 本质上也是锁,可以设定锁的数量

from multiprocessing import Process,Semaphore
import time
def ktv(person,sem):
    sem.acquire()
    print("%s开始唱歌了"%(person))
    time.sleep(0.3)
    print("%s唱完回家了"%(person))
    sem.release()

if __name__ == "__main__":
    sem = Semaphore(3) # 设置为一次允许3个进程进去
    for i in ["bob","jack","rose","lisa"]:
        p = Process(target=ktv,args=(i,sem))
        p.start()

2. 事件(Event): 阻塞事件,程和进程之间的数据彼此隔离,但是可以通过socket互相发消息

        e = Event()生成事件对象e
        e.wait()动态给程序加阻塞 , 程序当中是否加阻塞完全取决于该对象中的is_set() [默认返回值是False]
         如果是True 不加阻塞,如果是False 加阻塞

         控制这个属性的值
         set()方法 将这个属性的值改成True
         clear()方法 将这个属性的值改成False
        is_set()方法 判断当前的属性是否为True (默认上来是False)

2.1 模拟交通灯效果

# 模拟红绿灯效果
from multiprocessing import Process,Event
import random,time

def tarffic_light(e):
    print("红灯亮")
    while True:
        if e.is_set():  # 默认是False
            time.sleep(0.5)
            print("红灯亮")
            e.clear()
        else:
            time.sleep(1)
            print("绿灯亮")
            e.set()

def car(e,i):
    if not e.is_set(): # 与交通灯相对应,这里应该是停车状态
        print("车%s要等待"%(i))
        e.wait()

   print("车%s可以走了"%(i)) if __name__ == "__main__": e = Event() p1 = Process(target=tarffic_light,args=(e,)) p1.start() for i in ["红色","蓝色","黑色","白色","紫色","棕色","黄色","银色"]: time.sleep(random.randrange(0,2)) p2 = Process(target=car,args=(e,i)) p2.start() # 将红绿灯变成守护进程 from multiprocessing import Process,Event import random,time def tarffic_light(e): print("红灯亮") while True: if e.is_set(): # 默认是False time.sleep(0.5) print("红灯亮") e.clear() else: time.sleep(1) print("绿灯亮") e.set() def car(e,i): if not e.is_set(): # 与交通灯相对应,这里应该是停车状态 print("车%s要等待"%(i)) e.wait()

   print("车%s可以走了"%(i)) if __name__ == "__main__": e = Event() lst = [] p1 = Process(target=tarffic_light,args=(e,)) p1.daemon = True p1.start() for i in ["红色","蓝色","黑色","白色","紫色","棕色","黄色","银色"]: time.sleep(random.randrange(0,2)) p2 = Process(target=car,args=(e,i)) p2.start() lst.append(p2) for j in lst: j.join() print("全部通过")

 

3. 进程间的通信:

        实现进程之间通信的两种机制:管道 Pipe和队列 Queue

        put() 存放 
        get() 获取  用get()获取值时,如果队列中没有值了,程序会阻塞在这里
        get_nowait() 拿不到报异常
        put_nowait() 非阻塞版本的put  只能在Windows里用,Linux里用不了
        q.empty() 检测是否为空 (了解)
        q.full() 检测是否已经存满 (了解)

3.1 基本用法

from multiprocessing import Process,Queue
q = Queue(2)
q.put(1)
q.put(2)
# q.put(3) # 如果队列中只设定放2个值,当放入第三个值时,程序会阻塞,在等别的程序将值拿走,再放第三个值
res = q.get()
print(res)
q.get()
q.get()  # 同理,如果队列中只设定放2个值,当取出第三个值时,程序会阻塞,在等队列中有值取出再放入第三个值再打印

3.2 进程之间通过队列交换数据

from multiprocessing import Process,Queue
def func1(q):
    res1 = q.get()
    print(res1+"111")
    q.put("午餐")

if __name__ == "__main__":
    q = Queue()
    p = Process(target=func1,args=(q,))
    p.start()
    q.put("早餐")
    p.join()
    res2 = q.get()
    print(res2+"222")
# 早餐111
# 午餐222
3.3 生产者和消费者模型
from multiprocessing import Process,Queue

# 消费者
def consumer(q,name):
    while True:
        food = q.get()
        print("%s吃了%s"%(name,food))
# 生产者
def producer(q,food,name):
    print("%s生产了%s"%(name,food))
    q.put(food)

if __name__ == "__main__":
    q = Queue()
    p1 = Process(target=consumer,args=(q,"bob"))
    p1.start()
    for i in ["包子","凉皮","辣条","排骨汤","腊肠"]:
        p2 = Process(target=producer,args=(q,i,"jack"))
        p2.start()
# 此时程序并未停止,因为生产者模型里面的get没取到值,阻塞了程序


# 优化:发送None
from multiprocessing import Process,Queue

# 消费者
def consumer(q,name):
    while True:
        food = q.get()
        if food == None:
            break
        print("%s吃了%s"%(name,food))

# 生产者
def producer(q,food,name):
    print("%s生产了%s"%(name,food))
    q.put(food)

if __name__ == "__main__":
    q = Queue()
    lst = []
    p1 = Process(target=consumer,args=(q,"bob"))
    p1.start()
    for i in ["包子","凉皮","辣条","排骨汤","腊肠"]:
        p2 = Process(target=producer,args=(q,i,"jack"))
        p2.start()
        lst.append(p2)
    for j in lst:
        j.join()
    q.put(None)
# 但是发送None的话,每加一个生产者或者消费者进程,都需要发送一个None,太过麻烦

3.4 生产者和消费者模型最终优化版

JoinableQueue: 

put 一次 每存放一个值,队列计数器加1
get 一次 通过task_done让队列计数器减1
join 函数,会根据队列中的计数器来判定是阻塞还是放行
如果计数器变量是0,意味着放行,其他情况阻塞

from multiprocessing import Process,JoinableQueue
import time,random
# 生产者
def producer(jq,name,food):
    time.sleep(random.uniform(0.1,0.9))
    print("%s生产了%s"%(name,food))
    jq.put(food)

# 消费者
def consumer(jq,name):
    while True:
        food = jq.get()
        time.sleep(random.uniform(0.1,0.9))
        print("%s吃了%s"%(name,food))
        jq.task_done()

if __name__ == "__main__":
    lst = []
    jq = JoinableQueue()
    p1 = Process(target=consumer,args=(jq,"bob"))
    p1.daemon = True
    p1.start()
    for i in ["包子","凉皮","辣条","排骨汤","腊肠"]:
        p2 = Process(target=producer,args=(jq,"jack",i))
        p2.start()
        lst.append(p2)
    for j in lst:
        j.join()
    jq.join()

    print("收工,回家睡觉")

 4. Manager:  用于进程之间的数据共享(列表和字典)

from multiprocessing import Manager,Process,Lock

def func1(date,lock):
    lock.acquire()
    date["count"] -= 1
    lock.release()

def func2(date,lock):
    lock.acquire()
    date[0] += 1
    lock.release()

if __name__ == "__main__":
    m = Manager()  
    lock = Lock()
    dic1 = m.dict({"count":200})  # 字典形式
    date2 = m.list([1,2]) # 列表形式
    lst = []
    for i in range(10):
        p1 = Process(target=func1,args=(dic1,lock))
        p2 = Process(target=func2, args=(date2,lock))
        lst.append(p1)
        p1.start()
        p2.start()
    for j in lst:
        j.join()
    print(dic1)
    print(date2)
# {'count': 190}
# [11, 2] 若不加锁,可能导致数据错误

 

posted on 2020-06-10 21:22  fdsimin  阅读(145)  评论(0编辑  收藏  举报