使用互斥锁
from threading import Thread,Lock #从threading包内倒入Lock模块
mutex = Lock() #定义锁
x = 100
def task():
global x
mutex.acquire() # 获得锁
temp = x
x = temp - 1
mutex.release() # 释放锁
if __name__ == '__main__':
t_l = []
for i in range(100):
t = Thread(target=task)
t_l.append(t)
t.start()
for t in t_l:
t.join()
print(x) #最终结果就是0
了解知识:死锁和解决死锁的方法(递归锁) 信号量
死锁实例
import time
from threading import Thread, Lock
mutexA = Lock()
mutexB = Lock()
class Mythread(Thread):
def run(self):
self.f1()
self.f2()
def f1(self):
mutexA.acquire()
print('%s 拿到A锁' % self.name) #self.name是线程名
mutexB.acquire()
print('%s 拿到B锁' % self.name)
mutexB.release()
mutexA.release()
def f2(self):
mutexB.acquire()
print('%s 拿到B锁' % self.name)
time.sleep(1)
mutexA.acquire()
print('%s 拿到了A锁' % self.name)
mutexA.release()
mutexB.release()
if __name__ == '__main__':
for i in range(10):
t = Mythread()
t.start()
代码解读:
开始执行函数,此时10个线程同时起来了,第一个线程在执行run,run里面执行两个函数f1和f2,线程1拿到了A锁,线程1又拿到了B锁,此时程序再往下走线程1释放了A锁和B锁,紧接着执行f2函数,此时线程1拿到了B锁,然后睡了1秒的同时,线程2起来了,线程2执行f1,此时线程2拿到了A锁,线程2想要拿B锁,由于线程是异步的,所以此时B锁在线程1的f2里睡着(1秒),此时线程1一秒过后想要拿A锁,但是这时候A锁在线程2里面等着释放(线程2还在执行f1,等着拿到B锁再执行释放A,B锁)所以这时候线程1在执行f2代码的时候由于A锁再线程2手里,得不到A锁代码就走不下去(无法执行后面的释放代码),线程2想要拿B锁,由于B锁在线程1手里(无法执行后面的释放代码),所以就形成了死锁递归锁,程序执行不下去
解决死锁的办法(使用RLock递归锁)
import time
from threading import Thread, RLock #使用递归锁要倒入RLock模块
obj = RLock() #实例化一个变量等于RLock
mutexA = obj #obj赋值给变量
mutexB = obj
class Mythread(Thread):
def run(self):
self.f1()
self.f2()
def f1(self):
mutexA.acquire()
print('%s 拿到A锁' % self.name) #self.name是线程名
mutexB.acquire()
print('%s 拿到B锁' % self.name)
mutexB.release()
mutexA.release()
def f2(self):
mutexB.acquire()
print('%s 拿到B锁' % self.name)
time.sleep(1)
mutexA.acquire()
print('%s 拿到了A锁' % self.name)
mutexA.release()
mutexB.release()
if __name__ == '__main__':
for i in range(10):
t = Mythread()
t.start()
PS:RLock递归锁的特点就是可以连续的acquire(上锁)不需要release(释放),而通常的Lock不能连续acquire(上锁),必须release(释放)后才能再次acquire(上锁)
信号量实例
信号量是控制同一时刻并发执行的任务数(和线程池、进程池很相似)
from threading import Thread,Semaphore,current_thread
import time,random
sm = Semaphore(5) #括号内就是设置并发执行的任务数
def task():
with sm: # with 这个关键词就是把acquire和release合并成一步,自动执行,不需要写代码了
print('%s 正在上厕所' %current_thread().name)
time.sleep(random.randint(1,4))
if __name__ == '__main__':
for i in range(20):
t = Thread(target=task)
t.start()
PS:可以看到第一次最多同时执行了5个线程,然后是有几个线程优先结束,然后执行几个线程