线程的补充(锁、信号量、事件、条件、定时器、队列)

线程的补充

一、锁
1、数据安全问题
# 线程为什么要有锁
    # 线程之间的数据安全问题 :
        # += -= 赋值操作不安全
        
    # 线程安全的数据类型有:
        # pop append 都是线程安全的
        # 队列也是数据安全的

例子:数据不安全
from threading import Thread n = 0 def func(): global n for i in range(150000): n -= 1 def func2(): global n for i in range(150000): n += 1 t_lst = [] for i in range(10): t2 = Thread(target=func2) t = Thread(target=func) t.start() t2.start() t_lst.append(t) t_lst.append(t2) for t in t_lst: t.join() print('--->',n) # --->-17665 因此在进行 += -=操作的时候应该加上锁: from threading import Thread,Lock n = 0 def func(lock): global n for i in range(150000): lock.acquire() n -= 1 lock.release() def func2(lock): global n for i in range(150000): lock.acquire() n += 1 lock.release() lock = Lock() t_lst = [] for i in range(10): t2 = Thread(target=func2,args=(lock,)) t = Thread(target=func,args=(lock,)) t.start() t2.start() t_lst.append(t) t_lst.append(t2) for t in t_lst: t.join() print('--->',n) # --->0 2、死锁问题 所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法运行下去。 此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。即:操作的时候,抢到一把锁之后还要再去抢第二把锁, 但是由于一个线程抢到一把锁,另一个线程抢到了另一把锁,所以导致死锁。 例如:(互斥锁才会出现死锁) import time from threading import Thread,Lock noodle_lock = Lock() fork_lock = Lock() def eat1(name): noodle_lock.acquire() print('%s拿到面条了'%name) fork_lock.acquire() print('%s拿到叉子了' % name) print('%s吃面' % name) time.sleep(0.3) fork_lock.release() print('%s放下叉子' % name) noodle_lock.release() print('%s放下面'%name) def eat2(name): fork_lock.acquire() print('%s拿到叉子了' % name) noodle_lock.acquire() print('%s拿到面条了'%name) print('%s吃面'%name) time.sleep(0.3) noodle_lock.release() print('%s放下面'%name) fork_lock.release() print('%s放下叉子' % name) name_list = ['小明','小红'] name_list2 = ['小白','小黑'] for name in name_list: Thread(target=eat1,args=(name,)).start() for name in name_list2: Thread(target=eat2,args=(name,)).start() 结果: 解决方法,递归锁,在Python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。 这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。 上面的例子如果使用RLock代替Lock,则不会发生死锁: import time from threading import Thread,RLock fork_lock = noodle_lock = RLock() def eat1(name): noodle_lock.acquire() print('%s拿到面条了'%name) fork_lock.acquire() print('%s拿到叉子了' % name) print('%s吃面' % name) time.sleep(0.3) fork_lock.release() print('%s放下叉子' % name) noodle_lock.release() print('%s放下面'%name) def eat2(name): fork_lock.acquire() print('%s拿到叉子了' % name) noodle_lock.acquire() print('%s拿到面条了'%name) print('%s吃面'%name) time.sleep(0.3) noodle_lock.release() print('%s放下面'%name) fork_lock.release() print('%s放下叉子' % name) name_list = ['小明','小红'] name_list2 = ['小白','小黑'] for name in name_list: Thread(target=eat1,args=(name,)).start() for name in name_list2: Thread(target=eat2,args=(name,)).start() 3、死锁总结 互斥锁:每释放一把钥匙,别人立刻就可以抢 递归锁:要等全部钥匙释放完了,别人才能去抢,实际上是一串钥匙,只有一个人能得到

递归锁可以解决互斥锁的死锁问题 互斥锁 两把锁(一个门需要两把钥匙才能开,但可能出现一个人抢了一把钥匙,另一个人抢了另一把钥匙,导致大家都进不去) 多个线程抢 递归锁 一把锁(抢一串钥匙,第一把进了一个门,另一把钥匙再进第二个门...然后出第二个门,再出第一个门,完全出来后其他人才能抢这个钥匙串) 多个线程抢 递归锁好不好? 递归锁并不是一个好的解决方案 死锁现象的发生不是互斥锁的问题 而是程序员的逻辑有问题导致的 递归锁能够快速的解决死锁问题 递归锁 迅速恢复服务 递归锁替换互斥锁 在接下来的时间中慢慢把递归锁替换成互斥锁 能够完善代码的逻辑 提高代码的效率 多个线程之间,用完一个资源再用另外一个资源 先释放一个资源,再去获取一个资源的锁 二、信号量 三、事件 四、条件 五、定时器 六、队列

猜你喜欢

转载自www.cnblogs.com/Zzbj/p/9703811.html