zoukankan      html  css  js  c++  java
  • 死锁 递归锁 信号量 Event事件 线程q

    一 死锁与递归锁

    ​ 使用抢锁必须释放锁,其实在操作锁的时候也及其容易产生死锁现象(阻塞,即整个程序卡死)

    案例说明:

    from threading import Thread, Lock
    import time
    
    mutexA = Lock()
    mutexB = Lock()
    
    
    # 类只要加括号多次 产生的肯定是不同的对象
    # 如果你想要实现多次加括号等到的是相同的对象 单例模式
    
    
    class MyThead(Thread):
        def run(self):
            self.func1()
            self.func2()
    
        def func1(self):
            mutexA.acquire()
            print('%s 抢到A锁' % self.name)  # 获取当前线程名
            mutexB.acquire()
            print('%s 抢到B锁' % self.name)
            mutexB.release()
            print('%s 释放了B锁'%self.name)
            mutexA.release()
            print('%s 释放了A锁'%self.name)
    
    
        def func2(self):
            mutexB.acquire()
            print('%s 抢到B锁' % self.name)
            time.sleep(2)
            mutexA.acquire()
            print('%s 抢到A锁' % self.name)  # 获取当前线程名
            mutexA.release()
            print('%s 释放了A锁'%self.name)
            mutexB.release()
            print('%s 释放了B锁'%self.name)
    
    if __name__ == '__main__':
        for i in range(10):
            t = MyThead()
            t.start()
    
    #结果展示
    '''
    Thread-1 抢到A锁
    Thread-1 抢到B锁
    Thread-1 释放了B锁
    Thread-1 释放了A锁
    Thread-1 抢到B锁
    Thread-2 抢到A锁
    此时阻塞住了
    '''
    #阻塞原因:
    '''
    此时线程2想要抢A锁,但是A锁在线程1手上,线程1此时想要抢B 锁,但是B锁在在线程2手上
    所以此时产生了死锁现象,导致阻塞
    '''
    

    二 递归锁(了解)

    递归锁的特点:

    ​ 1、第一个抢到这把锁的人可以执行连续的acquire和 release操作(只能被第一个抢到锁的,且一个acquire对应一个 release)

    ​ 2、它的内部有一个计数器 每acquire一次计数加一,每realse 一次计数减一

    ​ 3、只要计数不为0,那么其它人都无法抢到该锁

    mutexA = Lock()
    mutexB = Lock()
    # 换成
    mutexA = mutexB = RLock()  #递归锁
    

    具体案例:

    from threading import Thread, RLock
    import time
    
    # mutexA = Lock()
    # mutexB = Lock()
    mutexA = mutexB = RLock()
    
    class MyThead(Thread):
        def run(self):
            self.func1()
            self.func2()
    
        def func1(self):
            mutexA.acquire()
            print('%s 抢到A锁' % self.name)  # 获取当前线程名
            mutexB.acquire()
            print('%s 抢到B锁' % self.name)
            mutexB.release()
            print('%s 释放了B锁'%self.name)
            mutexA.release()
            print('%s 释放了A锁'%self.name)
    
    
        def func2(self):
            mutexB.acquire()
            print('%s 抢到B锁' % self.name)
            time.sleep(2)
            mutexA.acquire()
            print('%s 抢到A锁' % self.name)  # 获取当前线程名
            mutexA.release()
            print('%s 释放了A锁'%self.name)
            mutexB.release()
            print('%s 释放了B锁'%self.name)
    
    if __name__ == '__main__':
        for i in range(3):
            t = MyThead()
            t.start()
    
    #结果展示
    '''
    Thread-1 抢到A锁
    Thread-1 抢到B锁
    Thread-1 释放了B锁
    Thread-1 释放了A锁
    Thread-1 抢到B锁
    Thread-1 抢到A锁
    Thread-1 释放了A锁
    Thread-1 释放了B锁
    Thread-2 抢到A锁
    Thread-2 抢到B锁
    Thread-2 释放了B锁
    Thread-2 释放了A锁
    Thread-2 抢到B锁
    Thread-2 抢到A锁
    Thread-2 释放了A锁
    Thread-2 释放了B锁
    Thread-3 抢到A锁
    Thread-3 抢到B锁
    Thread-3 释放了B锁
    Thread-3 释放了A锁
    Thread-3 抢到B锁
    Thread-3 抢到A锁
    Thread-3 释放了A锁
    Thread-3 释放了B锁
    '''
    

    三 信号量(了解)

    信号量在不同的阶段可能对应不同的技术点

    在并发编程中信号量指的是锁,相当于批量的锁

    """
    如果我们将互斥锁比喻成一个坑位的话
    那么信号量就相当于多个坑位
    """
    from threading import Thread, Semaphore
    import time
    import random
    
    sm = Semaphore(3)  # 括号内写数字 写几就表示开设几个坑位
    
    def task(name):
        sm.acquire()
        print('%s 正在蹲坑'% name)
        time.sleep(random.randint(1, 5))
        sm.release()
    
    if __name__ == '__main__':
        for i in range(9):
            t = Thread(target=task, args=('伞兵%s号'%i, ))
            t.start()
    
    #结果展示      
    '''
    伞兵0号 正在蹲坑
    伞兵1号 正在蹲坑
    伞兵2号 正在蹲坑
       手动换行,上面三个线程是一块拿到锁后打印的
    伞兵3号 正在蹲坑
    伞兵4号 正在蹲坑
    伞兵5号 正在蹲坑
    伞兵6号 正在蹲坑
    伞兵7号 正在蹲坑
    伞兵8号 正在蹲坑
    '''
    

    四 Event事件(了解)

    用于一些进程/线程需要等待另外一些进程/线程运行完毕之后才能运行,类似于发射信号一样

    event 的方法使用:

    event = Event()  # 得到一个event事件对象
    
    #在一个进程/线程中使用
    event.wait() #表示此线程阻塞,等待其他线程释放可以运行的信号,即event.set()
    
    #另一个进程/线程中使用
    event.set()  #表示此线程释放了运行信号,即其他由于 event.wait()阻塞的线程可以运行了
    
    
    

    具体案例:红绿灯

    from threading import Thread, Event
    import time
    
    event = Event()  # 得到一个event对象,造了一个红绿灯
    
    def light():
        print('红灯亮着的')
        time.sleep(3)
        print('绿灯亮了')
        event.set() # # 告诉等待红灯的人可以走了
    
    def car(name):
        print('%s 车正在灯红灯' % name)
        event.wait()  # 等待别人给你发信号,即程序运行了event.set()
        print('%s 车加油门飙车走了' % name)
    
    
    if __name__ == '__main__':
        t = Thread(target=light)
        t.start()
    
        for i in range(5):
            t = Thread(target=car, args=('%s' % i,))
            t.start()
    
    #结果展示
    '''
    红灯亮着的
    0 车正在灯红灯
    1 车正在灯红灯
    2 车正在灯红灯
    3 车正在灯红灯
    4 车正在灯红灯
    绿灯亮了
    3 车加油门飙车走了
    4 车加油门飙车走了
    1 车加油门飙车走了
    2 车加油门飙车走了
    0 车加油门飙车走了
    '''
    

    五 线程q(了解)

    同一个进程下多个线程数据是共享的,为什么在同一个进程下还是会去使用队列呢?

    因为队列是管道 + 锁,所以使用队列是为了保证数据的安全

    import queue
    
    # 我们现在使用的队列都是只能在本地测试使用
    
    # 1 队列q  先进先出
    # q = queue.Queue(3)
    # q.put(1)
    # q.get()
    # q.get_nowait()
    # q.get(timeout=3)
    # q.full()
    # q.empty()
    
    
    # 后进先出q(堆栈)
    # q = queue.LifoQueue(3)  # last in first out
    # q.put(1)
    # q.put(2)
    # q.put(3)
    # print(q.get())  # 3
    
    # 优先级q   你可以给放入队列中的数据设置进出的优先级
    q = queue.PriorityQueue(4)
    q.put((10, '111'))
    q.put((100, '222'))
    q.put((0, '333'))
    q.put((-5, '444'))
    print(q.get())  # (-5, '444')
    # put括号内放一个元祖  第一个放数字表示优先级
    # 需要注意的是 数字越小优先级越高!!!
    
  • 相关阅读:
    当我有一台服务器时我做了什么
    git 安装及基本配置
    关于大厂面试中问到的二十几个 HTTP 面试题
    日问周刊 | 全栈面试汇总 | 第七期
    dockerfile 最佳实践及示例
    面试官:如果 http 响应头中 ETag 值改变了,是否意味着文件内容一定已经更改
    Nginx 反向代理时获取用户的真实 IP
    Go 语言实现 HTTP 层面的反向代理
    Go 语言中的 Http 路由基础
    Json Schema
  • 原文地址:https://www.cnblogs.com/xy-han/p/12791266.html
Copyright © 2011-2022 走看看