死锁 递归锁 信号量 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括号内放一个元祖  第一个放数字表示优先级
# 需要注意的是 数字越小优先级越高!!!

猜你喜欢

转载自www.cnblogs.com/xy-han/p/12791266.html
今日推荐