• 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] 若不加锁,可能导致数据错误
  • 相关阅读:
    myeclipse自动生成相应对象接收返回值的快捷键
    JavaEE学习记录(一)--软件系统体系结构
    通过Java编码获取String分行字符串的内容
    长款或短款的处理(二)
    现金清查中的长款短款的简单解释(一)
    mybatis 的mapper配置文件sql语句中, 有时用到 大于, 小于等等
    <c:forEach items="${list}" var="tt" varStatus="status"> 的相关大小长度
    svn 提交 working copy is not up-to-date
    svn: Working copy 'D:workspaceweb....images' is too old (format 10, created by Subversion 1.6
    mybatis generator eclipse插件的安装
  • 原文地址:https://www.cnblogs.com/fdsimin/p/13089113.html
Copyright © 2020-2023  润新知