线程进阶
互斥锁与数据安全
线程之间的数据安全的问题:+= -= 赋值操作不安全
pop,append都是线程安全的 队列也是数据安全
1 from threading import Thread,Lock 2 n = 0 3 def func(lock): 4 global n 5 for i in range(1500000): 6 lock.acquire() 7 n -= 1 8 lock.release() 9 10 def func2(lock): 11 global n 12 for i in range(1500000): 13 lock.acquire() 14 n += 1 15 lock.release() 16 17 if __name__ == '__main__': 18 t_lst = [] 19 lock = Lock() 20 for i in range(10): 21 t2 = Thread(target=func2,args=(lock,)) 22 t = Thread(target=func,args=(lock,)) 23 t.start() 24 t2.start() 25 t_lst.append(t) 26 t_lst.append(t2) 27 for t in t_lst: 28 t.join() 29 print('-->',n) 30 #---> -13500000 31 #结果不完全正确,数据并不安全

递归锁

1 import time 2 from threading import Thread,Lock 3 4 noodle_lock = Lock() 5 fork_lock = Lock() 6 def eat1(name): 7 noodle_lock.acquire() 8 print('%s拿到面条了' % name) 9 fork_lock.acquire() 10 print('%s拿到叉子了' % name) 11 print('%s在吃面' % name) 12 time.sleep(0.5) 13 fork_lock.release() 14 print('%s放下叉子' % name) 15 noodle_lock.release() 16 print('%s放下面' % name) 17 18 def eat2(name): 19 fork_lock.acquire() 20 print('%s拿到叉子了' % name) 21 noodle_lock.acquire() 22 print('%s拿到面条了' % name) 23 print('%s吃面' % name) 24 time.sleep(0.5) 25 noodle_lock.release() 26 print('%s放下面' % name) 27 fork_lock.release() 28 print('%s放下叉子' % name) 29 30 if __name__ == '__main__': 31 name_list = ['小明', '小灰'] 32 name_list2 = ['小花', '小草'] 33 for name in name_list: 34 Thread(target=eat1, args=(name,)).start() 35 for name in name_list2: 36 Thread(target=eat2, args=(name,)).start()
死锁现象:两把锁 异步的 操作的时候抢到一把锁还要去抢第二把锁(抢到了面还要去抢叉子)
一个线程抢到一把锁(抢到了面),另一个线程抢到了另一把锁(抢到了叉子),大眼瞪小眼
1 import time 2 from threading import Thread,RLock 3 fork_lock = noodle_lock = RLock() 4 def eat1(name): 5 noodle_lock.acquire() 6 print('%s拿到面条了' % name) 7 fork_lock.acquire() 8 print('%s拿到叉子了' % name) 9 print('%s在吃面' % name) 10 time.sleep(0.5) 11 fork_lock.release() 12 print('%s放下叉子' % name) 13 noodle_lock.release() 14 print('%s放下面' % name) 15 16 def eat2(name): 17 fork_lock.acquire() 18 print('%s拿到叉子了' % name) 19 noodle_lock.acquire() 20 print('%s拿到面条了' % name) 21 print('%s吃面' % name) 22 time.sleep(0.5) 23 noodle_lock.release() 24 print('%s放下面' % name) 25 fork_lock.release() 26 print('%s放下叉子' % name) 27 28 if __name__ == '__main__': 29 name_list = ['小明', '小灰'] 30 name_list2 = ['小花', '小草'] 31 for name in name_list: 32 Thread(target=eat1, args=(name,)).start() 33 for name in name_list2: 34 Thread(target=eat2, args=(name,)).start()
递归锁可以快速解决互斥锁的死锁问题
互斥锁:两把锁,多个线程抢
递归锁:一把锁,多个线程抢
但是递归锁并不是一个好的解决方案,死锁的发生不是互斥锁的问题,而是程序员的逻辑导致的,只是递归锁可以快速解决死锁问题
递归锁:迅速恢复服务,递归锁替换成互斥锁
能够完善代码的逻辑并提高代码的效率
多个线程之间,用完一个资源再用另一个资源,先释放一个资源,再去获取一个资源
信号量
1 import time 2 from threading import Semaphore,Thread 3 4 def func(index,sem): 5 sem.acquire() 6 print(index) 7 time.sleep(1) 8 sem.release() 9 10 if __name__ == '__main__': 11 sem = Semaphore(5) #每个执行的个数 12 for i in range(10): 13 Thread(target=func, args=(i, sem)).start() 14 #与进程的信号量类似
事件
wait() 等待 事件内的信号变成True set() 把信号变成True clear() 把信号变成False is_set() 判断信号状态是否为True
1 import time 2 import random 3 from threading import Event,Thread 4 def check(e): 5 print('开始检测数据库是否连接') 6 time.sleep(random.randint(0, 5)) 7 e.set() 8 9 def connect(e): 10 for i in range(3): 11 e.wait(0.5) 12 if e.is_set(): 13 print('数据库连接成功') 14 break 15 else: 16 print('尝试连接数据库%s次失败' % (i+1)) 17 else: 18 raise TimeoutError #三次机会连不上就抛异常 19 20 e = Event() 21 Thread(target=check, args=(e,)).start() 22 Thread(target=connect, args=(e,)).start()
条件 Condition
允许一次多少人通过
wait 在门口等待的所有人 notify 控制流量,即每次可以通过的人数
wait notify 使用前后都需要加锁
1 from threading import Condition,Thread 2 def func(con,index): 3 print('%s在等待' % index) 4 con.acquire() 5 con.wait() 6 print('%s do something' % index) 7 con.release() 8 9 con = Condition() 10 for i in range(10): 11 t = Thread(target=func, args=(con, i)) 12 t.start() 13 con.acquire() 14 con.notify_all() #允许通过所有人 15 con.release() 16 ################# 17 count = 10 18 while count > 0: 19 num = int(input('>>>')) 20 con.acquire() 21 con.notify(num) 22 count -= num 23 con.release() 24 #自己输入可以通过多少人
定时器 Timer
1 from threading import Timer 2 def func(): 3 print('------------>') 4 5 t = Timer(3, func) #定时 3秒后执行func 6 t.start() 7 print('主线程') 8 # 主线程 ------------>
只起一个线程:做定时任务
crontab : Linux操作系统上做定时任务的工具
队列
线程安全 队列:排队相关的逻辑
与进程的队列类似:q.get() q.put() q.put_nowait() q.get_nowait()
qps 每秒接收的请求数 帮助你维持程序相应的顺序
1 from queue import LifoQueue 2 lq = LifoQueue() #栈---后入先出 3 lq.put(1) 4 lq.put(2) 5 lq.put(3) 6 lq.put('a') 7 lq.put('b') 8 print(lq.get()) 9 print(lq.get()) 10 print(lq.get()) 11 #b a 3 12 13 from queue import PriorityQueue 14 pq = PriorityQueue() 15 pq.put((15, 'abc')) 16 pq.put((5, 'ghi')) 17 pq.put((11, 'def')) 18 pq.put((11, 'abc')) 19 print(pq.get()) 20 print(pq.get()) 21 print(pq.get()) 22 #(5, 'ghi') (11, 'abc') (11, 'def') 23 #按照大小排队,如果一样按照后边的ascii 码排序,只加一个括号只输出第一个 24 #格式必须一致,如果是数字必须是数字,字母必须是字母,数字字母就是数字字母
#####################################################
# 线程
# 为什么要有锁
# 保证数据安全
# GIL保证只有一个线程能使用CPU
# 每个线程拥有GIL锁的时间是有限的
# 当一个线程已经到了GIL轮转的临界点的时候
# 线程的调度 —— 操作系统的
# 哪个线程处于就绪状态 —— Cpython解释器控制
# 数据不安全的本质 :一句python代码对应了多条cpu指令
# dis模块查看cpu指令 : load 读数据 func 放回去的过程
# 互斥锁和递归锁
# 互斥锁 :
# 在一个线程里 只用一个锁的时候 互斥锁
# 为了提高效率 锁多个资源的时候 应该酌情选用互斥锁
# 递归锁 :
# 在一个线程里 用多个锁的时候 递归锁
# 实例化一个锁 acquire两次
# 信号量和事件
# 条件和定时器
# 队列:优先级队列,栈,队列
# mulprocessing 队列 IPC进城之间通信 在文件级别的
# queue 队列 在内存级别的
import dis
# def func():
# a = 0
# a += 1
# dis.dis(func)
清风深知杨柳意,啤酒龙虾难相聚。

浙公网安备 33010602011771号