1.互斥锁
import time from threading import Thread,Lock def func1(lock): global n lock.acquire() #加锁 temp = n time.sleep(0.2) n = temp -1 lock.release() #解锁 n = 10 t_list = [] lock = Lock() for i in range(10): t1 = Thread(target=func1,args=(lock,)) t1.start() t_list.append(t1) for i in t_list: i.join() print(n)
结果: 牺牲了执行的速度,但保证了数据的安全性。
2.递归锁
from threading import Thread,RLock def func1(name): lock1.acquire() print('{}拿到第一把钥匙!'.format(name)) lock2.acquire() print('{}拿到第二把钥匙!'.format(name)) print('{}进入了房间'.format(name)) lock2.release() #归还第二把钥匙 lock1.release() #归还第一把钥匙 lock1 = RLock() #锁1 lock2 = RLock() #锁2 for i in range(10): Thread(target=func1,args=(i,)).start()
结果: 当同时需要拿到两把钥匙才可以执行代码时,容易出现分别两个不同的线程,每个线程各拿到一把钥匙时,就会出现死锁,所有递归锁就可以解决这样的问题。
3.信号量
from threading import Semaphore,Thread import time def func(sem,a,b): sem.acquire() time.sleep(1) #增加1秒之的间隔,更容易看出执行结果 print(a+b) sem.release() sem = Semaphore(4) #定义一个信号量对象,设置好信号量的数量 for i in range(10): t = Thread(target=func,args=(sem,i,i+5)) #将信号来传递进子线程要执行的函数 t.start()
结果:只能同时有4个进程访问函数。
4.事件
当事件被创建出来的时候,他的状态是False,事件的状态可以控制wait(),当事件的状态为True时,wait()非阻塞,当事件状态为False时,wait()阻塞。
有两个方法可以修改事件的状态,clear():设置事件的状态为False,set():设置事件的状态为True。
应用:多线程模拟检测数据库链接状态:
import time import random from threading import Thread,Event def connect_db(e): count = 0 #计数器 while count < 3: e.wait(1) #根据事件e的状态选择是否阻塞,括号里面加参数,设置阻塞时间1秒 if e.is_set() == True: #is_set():检测事件e的状态 print('数据库链接成功!') break else: count += 1 print('第{}次链接数据库失败!'.format(count)) else: print('连接数据库超时!') def check_web(e): time.sleep(random.randint(0,3)) #模拟网络延时0-3s e.set() #将事件对象状态设置为True e = Event() #创建一个事件对象 t1 = Thread(target=connect_db,args=(e,)) t2 = Thread(target=check_web,args=(e,)) t1.start() t2.start()
结果:
5.条件
条件类似一个高级的锁,他除了acquire()方法和release()方法之后还有两个方法,wait()方法和notify()方法。
一个条件被创建之初,默认会有一个状态,这个状态会影响wait()方法,一直处于等待状态。
notify(int数据类型)方法的括号中有一个int类型的数值,这个方法的作用是制作钥匙,括号中填写制作钥匙的数量。
wait和notify 都需要在acquire和release之间。
from threading import Thread,Condition def func(con,i): con.acquire() con.wait() #等待 print('在第{}个函数中!'.format(i)) con.release() con = Condition() #创建一个条件对象 for i in range(10): Thread(target=func,args=(con,i)).start() while True: num = int(input('输入一个整数:')) con.acquire() con.notify(num) #造钥匙 con.release()
结果: 造多少个钥匙,就多少个线程访问函数。
6.定时器
from threading import Timer def func(): print('定时开启线程!') #两个值 第一个:时间,以秒为单位,第二个:函数名 Timer(3,func).start() #3秒周之后开启一个线程执行函数func
结果:3秒后,打印‘定时开启线程!
7.队列
队列是安全的,队列中的数据先进先出。
import queue q = queue.Queue(10) # 创建一个队列对象,设置队列大小 for i in range(10): q.put(i) # 向队列中存数据 for i in range(10): print(q.get()) # 从队列中获取数据
结果:
当队列中没有值时,还继续用get方法获取队列中的值,会一直等待,当队列中值满了的时候,还继续用put方法想队列中存放数据,也会一直阻塞。
put_nowait()方法:
import queue q = queue.Queue(10) # 创建一个队列对象,设置队列大小 for i in range(11): q.put(i) # 向队列中存数据 q.put_nowait() # 当队列中值满了的时候,再继续向队列中存值,会报错
结果:
get_nowait()方法:
import queue q = queue.Queue(10) q.put('wdc') print(q.get()) q.get_nowait() # 当队列中没有值的时候,会报错
结果:
8.栈
先进后出。
import queue q = queue.LifoQueue(10) # 创建一个栈对象,大小为10 for i in range(10): q.put(i) for i in range(10): print(q.get())
结果:
9.优先级队列
import queue q = queue.PriorityQueue() # 创建一个优先级队列对象 q.put((20,'a')) # 向优先级队列放数据时,要放一个元组,第一个代表优先级,第二个是要存放的数据 q.put((30,'b')) q.put((10,'c')) print(q.get())
结果: 先获取优先级数值小的元组。
import queue q = queue.PriorityQueue() # 创建一个优先级队列对象 q.put((10,'s')) q.put((20,'a')) # 向优先级队列放数据时,要放一个元组,第一个代表优先级,第二个是要存放的数据 q.put((30,'b')) q.put((10,'c')) q.put((10,'d')) print(q.get())
结果: 当优先级相同时,会根据值的ascii编码的顺序获取。