1.互斥锁
importtimefrom threading importThread,Lockdeffunc1(lock):globaln
lock.acquire()#加锁
temp =n
time.sleep(0.2)
n= temp -1lock.release()#解锁
n= 10t_list=[]
lock=Lock()for i in range(10):
t1= Thread(target=func1,args=(lock,))
t1.start()
t_list.append(t1)for i int_list:
i.join()print(n)
结果:
牺牲了执行的速度,但保证了数据的安全性。
2.递归锁
from threading importThread,RLockdeffunc1(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 importSemaphore,Threadimporttimedeffunc(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。
应用:多线程模拟检测数据库链接状态:
importtimeimportrandomfrom threading importThread,Eventdefconnect_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('连接数据库超时!')defcheck_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 importThread,Conditiondeffunc(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()whileTrue:
num= int(input('输入一个整数:'))
con.acquire()
con.notify(num)#造钥匙
con.release()
结果:
造多少个钥匙,就多少个线程访问函数。
6.定时器
from threading importTimerdeffunc():print('定时开启线程!')#两个值 第一个:时间,以秒为单位,第二个:函数名
Timer(3,func).start() #3秒周之后开启一个线程执行函数func
结果:3秒后,打印‘定时开启线程!
7.队列
队列是安全的,队列中的数据先进先出。
importqueue
q= queue.Queue(10) #创建一个队列对象,设置队列大小
for i in range(10):
q.put(i)#向队列中存数据
for i in range(10):print(q.get()) #从队列中获取数据
结果:
当队列中没有值时,还继续用get方法获取队列中的值,会一直等待,当队列中值满了的时候,还继续用put方法想队列中存放数据,也会一直阻塞。
put_nowait()方法:
importqueue
q= queue.Queue(10) #创建一个队列对象,设置队列大小
for i in range(11):
q.put(i)#向队列中存数据
q.put_nowait() #当队列中值满了的时候,再继续向队列中存值,会报错
结果:
get_nowait()方法:
importqueue
q= queue.Queue(10)
q.put('wdc')print(q.get())
q.get_nowait()#当队列中没有值的时候,会报错
结果:
8.栈
先进后出。
importqueue
q= queue.LifoQueue(10) #创建一个栈对象,大小为10
for i in range(10):
q.put(i)for i in range(10):print(q.get())
结果:
9.优先级队列
importqueue
q= queue.PriorityQueue() #创建一个优先级队列对象
q.put((20,'a')) #向优先级队列放数据时,要放一个元组,第一个代表优先级,第二个是要存放的数据
q.put((30,'b'))
q.put((10,'c'))print(q.get())
结果:
先获取优先级数值小的元组。
importqueue
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编码的顺序获取。