zoukankan      html  css  js  c++  java
  • 进程同步控制 —— 锁信号量事件

    锁 —— multiprocessing.Lock  

           通过刚刚的学习,我们千方百计实现了程序的异步,让多个任务可以同时在几个进程中并发处理,他们之间的运行没有顺序(或者说由操作系统调度决定他们的顺序),一旦开启也不受我们控制。尽管并发编程让我们能更加充分的利用IO资源,但是也给我们带来了新的问题。

      当多个进程使用同一份数据资源的时候,就会引发数据安全或顺序混乱问题。

      加锁的形式确保了程序的顺序执行,但是执行又变成了串行,降低了效率,但是不得不说,它确保了数据的安全性。

      l = Lock()

      一把钥匙配一把锁

      拿钥匙,锁门   l.acquire ( acquire: 获取 )

      还钥匙,开门   l.release (release: 释放)

    from multiprocessing import Lock
    
    l = Lock()
    
    l.acquire()# 拿走钥匙,锁门,不让其他人进屋
    
    l.release()# 释放锁。  还钥匙,开门,允许其他人进屋
    
    # -*- coding:utf-8 -*-
    # Author : Niuli
    # Data : 2018/8/21 下午3:59
    from multiprocessing import Process, Lock, Value
    import time
    
    
    def get_money(num, l):
        l.acquire()  # 拿走钥匙,锁门
        for i in range(100):  # 100块,每次取1块
            num.value -= 1
            print('取:', num.value)
            time.sleep(0.01)
        l.release()  # 还钥匙,开门,下一位可以进入
    
    
    def put_money(num, l):
        l.acquire()  # 拿走钥匙,关上门
        for i in range(101):  # 100块,每次存1块
            num.value += 1
            print('存:', num.value)
            time.sleep(0.01)
        l.release()  # 还钥匙.开门,下一位可以进入
    
    
    if __name__ == '__main__':
        num = Value('i', 100)  # 类似 struck 需要一个类型,一个数据
        l = Lock()  # 实例化Lock的对象l (需要先制造一把锁)
        p_get = Process(target=get_money, args=(num, l))  # 创建取钱的进程,传两个参数:钱数,锁
        p_put = Process(target=put_money, args=(num, l))  # 创建存钱的进程,传两个参数:钱数,锁
        p_get.start()  # 开启取钱的进程
        p_put.start()  # 开启存钱的进程
        # 没有join 会异步执行,主程序会先执行完,打印最终的钱数 会是最开始的钱数
        # 没有join会先执行完主程序
        # 有join,主程序会等着子程序执行完后再接着执行
        p_get.join()  # 异步转同步,让子进程先执行,主程序等着  
        p_put.join()  # 异步转同步,让子进程先执行,主程序等着
        print('这里是最终的钱数:', num.value)
    银行存取钱问题
    取: 99
    取: 98
    取: 97
    取: 96
    取: 95
    取: 94
    取: 93
    取: 92
    取: 91
    取: 90
    取: 89
    取: 88
    取: 87
    取: 86
    取: 85
    取: 84
    取: 83
    取: 82
    取: 81
    取: 80
    取: 79
    取: 78
    取: 77
    取: 76
    取: 75
    取: 74
    取: 73
    取: 72
    取: 71
    取: 70
    取: 69
    取: 68
    取: 67
    取: 66
    取: 65
    取: 64
    取: 63
    取: 62
    取: 61
    取: 60
    取: 59
    取: 58
    取: 57
    取: 56
    取: 55
    取: 54
    取: 53
    取: 52
    取: 51
    取: 50
    取: 49
    取: 48
    取: 47
    取: 46
    取: 45
    取: 44
    取: 43
    取: 42
    取: 41
    取: 40
    取: 39
    取: 38
    取: 37
    取: 36
    取: 35
    取: 34
    取: 33
    取: 32
    取: 31
    取: 30
    取: 29
    取: 28
    取: 27
    取: 26
    取: 25
    取: 24
    取: 23
    取: 22
    取: 21
    取: 20
    取: 19
    取: 18
    取: 17
    取: 16
    取: 15
    取: 14
    取: 13
    取: 12
    取: 11
    取: 10
    取: 9
    取: 8
    取: 7
    取: 6
    取: 5
    取: 4
    取: 3
    取: 2
    取: 1
    取: 0
    存: 1
    存: 2
    存: 3
    存: 4
    存: 5
    存: 6
    存: 7
    存: 8
    存: 9
    存: 10
    存: 11
    存: 12
    存: 13
    存: 14
    存: 15
    存: 16
    存: 17
    存: 18
    存: 19
    存: 20
    存: 21
    存: 22
    存: 23
    存: 24
    存: 25
    存: 26
    存: 27
    存: 28
    存: 29
    存: 30
    存: 31
    存: 32
    存: 33
    存: 34
    存: 35
    存: 36
    存: 37
    存: 38
    存: 39
    存: 40
    存: 41
    存: 42
    存: 43
    存: 44
    存: 45
    存: 46
    存: 47
    存: 48
    存: 49
    存: 50
    存: 51
    存: 52
    存: 53
    存: 54
    存: 55
    存: 56
    存: 57
    存: 58
    存: 59
    存: 60
    存: 61
    存: 62
    存: 63
    存: 64
    存: 65
    存: 66
    存: 67
    存: 68
    存: 69
    存: 70
    存: 71
    存: 72
    存: 73
    存: 74
    存: 75
    存: 76
    存: 77
    存: 78
    存: 79
    存: 80
    存: 81
    存: 82
    存: 83
    存: 84
    存: 85
    存: 86
    存: 87
    存: 88
    存: 89
    存: 90
    存: 91
    存: 92
    存: 93
    存: 94
    存: 95
    存: 96
    存: 97
    存: 98
    存: 99
    存: 100
    存: 101
    这里是最终的钱数: 101
    结果
    from multiprocessing import Process, Lock
    import time
    
    
    def check(i):  # 查看不需要上锁
        with open('余票') as f:
            content = f.read()
        print('第%s个人查到余票还剩%s张' % (i, content))
    
    
    def buy(i, l):
        l.acquire()  # 拿钥匙,锁门
        with open('余票') as f:
            content = int(f.read())
    
        if content > 0:
            print('33[31m 第%s个人买到了票33[0m' % i)
            content -= 1
        else:
            print('第%s个人没有抢到' % i)
    
        time.sleep(0.1)  # 买完票后,把余票数量重写写入数据库的时间延迟
        with open('余票', 'w') as f:
            f.write(str(content))
        l.release()
    
    
    if __name__ == '__main__':
        l = Lock()
        for i in range(10):
            # 10个人查票,建立10个查票进程
            p_check = Process(target=check, args=(i + 1,))
            p_check.start()  # 开启进程
    
        for i in range(10):
            # 10个人抢票,建立10个抢票进程
            p_buy = Process(target=buy, args=(i + 1, l))
            p_buy.start()
    抢票

    信号量 —— multiprocessing.Semaphore

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

    上述讲的Lock,属于互斥锁,也就是一把钥匙配备一把锁,同时只允许锁住某一个数据。而信号量则是多把钥匙配备多把锁,也就是说同时允许锁住多个数据。
    
    比如在一个粉红发廊,里边有5位服务人员,那么这个发廊最多就同时允许进入5位客人,当又有第6位客人来的时候,就需要在门外等待;当服务人员服务完某位客人后,才允许后续的人再进来一个,换句话说,这个发廊最多同时接待5位客人,多的客人必须等待。
    
    信号量同步基于内部计数器,用户初始化一个计数器初值(比如上述例子中就初始化为5),每调用一次acquire(),计数器减1;每调用一次release(),计数器加1。当计数器为0时,acquire()调用被阻塞。这是迪科斯彻(Dijkstra)信号量概念P()和V()的Python实现。信号量同步机制适用于访问像服务器这样的有限资源。信号量与进程池的概念很像,但是要区分开,信号量涉及到加锁的概念
    
    信号量 Semaphore
    多把钥匙锁住多把锁
    from multiprocessing import Process, Semaphore
    import time
    import random
    
    
    def func(i, sem):
        sem.acquire()  # 获取钥匙
        print('第%s个人进入小黑屋,拿了钥匙锁了门' % i)
        time.sleep(random.randint(3, 5))
        print('第%s个人离开小黑屋,还了钥匙,打开了门' % i)
        sem.release()  # 还钥匙
    
    
    if __name__ == '__main__':
        sem = Semaphore(4)  # 允许4个进程执行 进入小黑屋
        # 之后其他人必须等待,有人出来 还了钥匙, 才允许后边的程序进入
        for i in range(1,21):
            p = Process(target=func, args=(i, sem))  # 创建20个禽兽
            p.start()
    发廊

    事件 —— multiprocessing.Event

    事件处理的机制:全局定义了一个“Flag”(event.is_set()),如果“Flag”值为 False,那么当程序执行 event.wait 方法时就会阻塞,如果“Flag”值为True,那么event.wait 方法时便不再阻塞。

    python中的事件机制,主要用于主进程控制其他进程的执行,事件主要提供了三个方法 set、wait、clear。
    
        事件处理的机制:全局定义了一个“Flag”(event.is_set()),如果“Flag”值为 False,那么当程序执行 event.wait 方法时就会阻塞,如果“Flag”值为True,那么event.wait 方法时便不再阻塞。
    
    clear:将“Flag”设置为False
    set:将“Flag”设置为True
    is_set:返回全局‘Flag’的bool值
    
    事件 Event
    事件 Event
    from multiprocessing import Process,Event
    
    e = Event() # 默认为False
    e.set()     
    e.clear()   
    e.wait()    
    e.is_set()
    
    # 事件是通过is_set()的bool值,去标识e.wait() 的阻塞状态
    # 当is_set()的bool值为False时,e.wait()是阻塞状态
    # 当is_set()的bool值为True时,e.wait()是非阻塞状态
    # 当使用set()时,是把is_set的bool变为True
    # 当使用clear()时,是把is_set的bool变为False
    
    print(e.is_set())# False wait应该是阻塞住
    e.set()# 将is_set 的bool值变为True,将wait变为非阻塞
    e.wait()
    print(e.is_set())
    print(123)
    e.clear()
    print(e.is_set())
    e.wait()
    print(123)
    举例
    from multiprocessing import Process,Event
    import time
    import random
    
    def tra(e):
        '''信号灯函数'''
        # e.set()
        # print('33[32m 绿灯亮! 33[0m')
        while 1:# 红绿灯得一直亮着,要么是红灯要么是绿灯
            if e.is_set():# True,代表绿灯亮,那么此时代表可以过车
                time.sleep(5)# 所以在这让灯等5秒钟,这段时间让车过
                print('33[31m 红灯亮! 33[0m')# 绿灯亮了5秒后应该提示到红灯亮
                e.clear()# 把is_set设置为False
            else:
                time.sleep(5)# 此时代表红灯亮了,此时应该红灯亮5秒,在此等5秒
                print('33[32m 绿灯亮! 33[0m')# 红的亮够5秒后,该绿灯亮了
                e.set()# 将is_set设置为True
    
    def Car(i,e):
        e.wait()# 车等在红绿灯,此时要看是红灯还是绿灯,如果is_set为True就是绿灯,此时可以过车
        print('第%s辆车过去了'%i)
    
    if __name__ == '__main__':
        e = Event()
        triff_light = Process(target=tra,args=(e,))# 信号灯的进程
        triff_light.start()
        for i in range(50):# 描述50辆车的进程
            if i % 3 == 0:
                time.sleep(2)
            car = Process(target=Car,args=(i+1,e,))
            car.start()
    信号灯模拟
  • 相关阅读:
    ASP.NET MVC WebAPI 上传图片实例
    PowerDesigner设计权限控制数据模型
    ASP.NET中使用WebService异步加载数据显示到页面
    C#+Dotnetbar海益ERP数据管理系统
    centos 6.X minimal 系列最小化安装完成后,安装mono和jexus过程小记录
    MVC3/4伪静态 jexus mvc伪静态
    petapoco 使用 MiniProfiler Glimpse监控
    尝试整理出自己的开发框架1
    初尝Brnshop移植到Linux Mono Jexus环境运行
    (转)Android开发出来的APP在手机的安装路径是?
  • 原文地址:https://www.cnblogs.com/niuli1987/p/9511656.html
Copyright © 2011-2022 走看看