线程进阶

互斥锁与数据安全

线程之间的数据安全的问题:+=   -= 赋值操作不安全   

  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()
Rlock递归锁

递归锁可以快速解决互斥锁的死锁问题

  互斥锁:两把锁,多个线程抢

  递归锁:一把锁,多个线程抢

但是递归锁并不是一个好的解决方案,死锁的发生不是互斥锁的问题,而是程序员的逻辑导致的,只是递归锁可以快速解决死锁问题

递归锁:迅速恢复服务,递归锁替换成互斥锁

    能够完善代码的逻辑并提高代码的效率

多个线程之间,用完一个资源再用另一个资源,先释放一个资源,再去获取一个资源

信号量

 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 #与进程的信号量类似
Semaphore

事件

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 #自己输入可以通过多少人
条件Condition

 定时器  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 # 主线程            ------------>
Timer

只起一个线程:做定时任务

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)
posted @ 2018-09-25 16:14  .why  阅读(253)  评论(0)    收藏  举报
Live2D