• Python 之并发编程之进程中(守护进程(daemon)、锁(Lock)、Semaphore(信号量))


    五:守护进程

    正常情况下,主进程默认等待子进程调用结束之后再结束
    守护进程在主进程所有代码执行完毕之后,自动终止
    kill -9 进程号 杀死进程.

    守护进程的语法:
    进程对象.daemon = True
    设置该进程为守护进程
    必须要写在start()方法之前赋值
    为主进程守护,主进程如果代码执行结束了,该守护进程自动结束

    #(1)基本语法
    #例:

    from multiprocessing import Process
    import time
    def func():
        print("子进程start")
        time.sleep(1)
        print("子进程end")
    
    if __name__ == "__main__":
        p = Process(target=func)
        p.daemon = True
        p.start()
        time.sleep(1)
        print("主进程执行结束")

    结果为:

    子进程start

    主进程执行结束

     

    因为为守护主进程,主进程结束子进程立即结束

    (2) 多个子进程的情况
    当多个子进程并发执行时,默认主进程等待子进程
    如果标记该子进程是守护进程
    当主进程执行完毕所有代码之后,守护进程立刻终止
    主进程的代码执行到最后一行,就意味着函数代码执行完毕,就应该杀掉守护进程
    其他非守护进程继续正常执行,主进程仍然等待直到结束
    最后主进程在真正的释放结束
    #例:

    from multiprocessing import Process
    import time
    def func1():
        count = 1
        while True:
            print("*" * count)
            time.sleep(0.5)
            count +=1
    
    def func2():
        print("func2 start")
        time.sleep(3)
        print("func2 end")
    
    if __name__ == "__main__":
        p1 = Process(target=func1)
        p1.daemon = True
        p1.start()
    
        p2 = Process(target=func2)
        p2.start()
    
        print("主进程代码执行结束...")

    运行结果为:

    主进程代码执行结束...
    func2 start
    func2 end
    3)守护进程的实际作用:监控报活

    #例:

    from multiprocessing import Process
    import time
    def alive():
        # 服务器向另外一个服务器发送消息
        # 给监控服务器发送报活消息
        # 如果监控服务器没有收到报活消息,就判定1号主机异常
        while True:
            print("1号服务主机...I am OK")
            time.sleep(0.8)
    
    def func():
        print("用来统计当前主机的服务器日志,打包变成文件")
        time.sleep(3)
    
    if __name__ == "__main__":
        p1 = Process(target=alive)
        p1.daemon = True
        p1.start()
    
        p2 = Process(target=func)
        p2.start()
    
        # 模拟好比这个程序结束了,那么服务器挂掉了
        p2.join()
        print("... ... ...")

    输出结果为:

    1号服务主机...I am OK

    用来统计当前主机的服务器日志,打包变成文件

    1号服务主机...I am OK

    1号服务主机...I am OK

    1号服务主机...I am OK

    ... ... ...

     

    首先由p2的阻塞,p2没有执行结束前,p2.join()后的主进程语句执行不了,等p2结束执行join后面的,然后主进程程序结束,在异步运行的p1守护进程随即结束运行。

    六: (Lock)

    lock.acquire()# 上锁

    lock.release()# 解锁

     

    #同一时间允许一个进程上一把锁 就是Lock

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

    #同一时间允许多个进程上多把锁 就是[信号量Semaphore]

    信号量是锁的变形: 实际实现是 计数器 + 锁,同时允许多个进程上锁

    # 互斥锁Lock:互斥锁就是进程的相互排斥
    #谁先抢到自由,谁就上锁改资源内容,为了保证数据的同步性
    #注意: 多个锁一起上,不开锁,会造成死锁,上锁和解锁是一对的

    1)锁的基本用法

    from multiprocessing import Process, Lock
    #例1:
    # 创建一把锁
    lock = Lock()
    # 上锁
    lock.acquire()
    print("111")
    
    # 解锁
    lock.release()
    
    #例2:
    # 死锁 : 只上锁,不解锁,会阻塞,产生死锁
    lock.acquire()
    print(111)
    lock.release()
    
    print(222)
    
    lock.acquire()
    print(333)
    #lock.release()
    lock.acquire()
    print(444)

    2的输出结果没有444,因为没有上一个锁解一个锁,造成死锁

    (1)模拟抢票软件

    比如12306抢票软件,实际上是吧mysql数据库中的内容读出来先与存在redis 的内存中。
    内存的读写的速度极快,不需要再刻意的加延迟等待,即可把该有的数据更新或者返回。
    等待抢票的高峰期过了,在慢慢把内存中的数据更新回MySQL当中。

    #例:(ticket内的内容为:{"count": 5},让十个人抢票)

    from multiprocessing import Process,Lock
    import json,time
    # 读取票数,更新票数
    def wr_info(sign,dic=None):
        if sign == "r":
            with open("ticket",mode="r",encoding="utf-8") as fp:
                dic = json.load(fp)
            return dic
        elif sign == "w":
            with open("ticket",mode="w",encoding="utf-8") as fp:
                json.dump(dic,fp)
    
    # 获取实际抢票的逻辑
    def get_ticket(person):
        # 读取数据库的实际票数
        dic = wr_info("r")
        # with open("ticket",mode="r",encoding="utf-8") as fp:
            # dic = json.load(fp)
        time.sleep(0.5)
        if dic['count'] > 0:
            print("%s抢到票了" % (person))
            dic["count"] -= 1
            # 更新数据库
            wr_info("w",dic)
            # with open("ticket",mode="w",encoding="utf-8") as fp:
                # json.dump(dic,fp)
        else:
            print("%s没有买到这个票" % (person))
    
    # 用ticket来进行函数的统一调用
    def ticket(person,lock):
        # 查询票数
        dic = wr_info("r")
        # with open("ticket",mode="r",encoding="utf-8") as fp:
        #     dic = json.load(fp)
        print("%s 查询余票 : %s" % (person,dic["count"]))
        lock.acquire()
    
        # 开始抢票
        get_ticket(person)
        lock.release()
    
    if __name__ == "__main__":
        lock = Lock()
        for i in range(10):
            p = Process(target=ticket,args=("person%s" % (i),lock) )
            p.start() 

    输出结果为:

    person2 查询余票 : 5

    person1 查询余票 : 5

    person6 查询余票 : 5

    person7 查询余票 : 5

    person0 查询余票 : 5

    person4 查询余票 : 5

    person9 查询余票 : 5

    person3 查询余票 : 5

    person5 查询余票 : 5

    person8 查询余票 : 5

    person2抢到票了

    person1抢到票了

    person6抢到票了

    person7抢到票了

    person0抢到票了

    person4没有买到这个票

    person9没有买到这个票

    person3没有买到这个票

    person5没有买到这个票

    person8没有买到这个票

     

    查询剩余票数是并发的一起的,在抢票的时候也是一起抢的,谁抢到了就会对剩余票数进行更新,抢到票的就会对剩余票数加锁,已免数据错乱。

    (3)区分同步和异步
    # 在产生进程对象的时候,进程之间是异步,上锁之后,进程之间变成同步

    from multiprocessing import Process,Lock
    def func(num, lock):
        lock.acquire()
        print("走到上锁这个地方,变成一个同步程序,先来的进行先执行,后来的进程后执行,按次序依次执行")
        print(num)
        lock.release()
    
    
    if __name__ == "__main__":
        # lock互斥锁, 进程之间数据不共享
        # 但是lock对象底层是通过socket来互相发送数据,不管多少进程,都是同一个lock锁
        lock = Lock()
        for i in range(10):
            p = Process(target=func, args=(i, lock))
            # 1. 10个子进程异步执行,是并发操作
            p.start()

    七.Semaphore(信号量)

    信号量Semaphore是一个计数器,控制对公共资源或者临界区域的访问量,信号量可以指定同时访问资源或者进入临界区域的进程数。每次有一个进程获得信号量时,计数器-1,若计数器为0时,其他进程就停止访问信号量,一直阻塞直到其他进程释放信号量。

    上述讲的Lock,属于互斥锁,也就是一把钥匙配备一把锁,同时只允许锁住某一个数据。而信号量则是多把钥匙配备多把锁,也就是说同时允许锁住多个数据。

    #例:

    from multiprocessing import Process,Semaphore
    import random,time
    
    def ktv(person,sem):
        # 上锁
        sem.acquire()
        print("%s进入ktv唱歌" % (person))
        time.sleep(random.randrange(3,8))
        print("%s走出ktv离开" % (person))
        # 解锁
        sem.release()
    
    
    if __name__ == "__main__":
        # 同一时间最多允许4个进程执行ktv任务,剩下的进程等待
        sem = Semaphore(4)
        for i in range(10):
            p = Process( target=ktv,args=("person%s" %(i),sem))
            p.start()

    每次运行4个进程,打一次打印是:

    person2进入ktv唱歌
    person1进入ktv唱歌
    person4进入ktv唱歌
    person0进入ktv唱歌

    紧接着:

    person2进入ktv唱歌
    person1进入ktv唱歌
    person4进入ktv唱歌
    person0进入ktv唱歌
    person1走出ktv离开
    person8进入ktv唱歌
    person4走出ktv离开
    person9进入ktv唱歌

    最后所有打印为:

    person2进入ktv唱歌
    person1进入ktv唱歌
    person4进入ktv唱歌
    person0进入ktv唱歌
    person1走出ktv离开
    person8进入ktv唱歌
    person4走出ktv离开
    person9进入ktv唱歌
    person2走出ktv离开
    person3进入ktv唱歌
    person0走出ktv离开
    person6进入ktv唱歌
    person9走出ktv离开
    person7进入ktv唱歌
    person8走出ktv离开
    person5进入ktv唱歌
    person3走出ktv离开
    person6走出ktv离开
    person5走出ktv离开
    person7走出ktv离开

     

  • 相关阅读:
    Ural 1966 Cycling Roads
    SQL Server 2008 安装(lpt亲测)
    cf Round#273 Div.2
    poj 2318 TOYS
    计算几何好模板
    ❤Friends
    限制pyqt5应用程序 只允许打开一次
    pyqt5 菜单栏+信息提示框
    Android Linux deploy
    system分区解锁
  • 原文地址:https://www.cnblogs.com/hszstudypy/p/10992829.html
Copyright © 2020-2023  润新知