python互斥锁与死锁
陈小c 人气:0一、多线程共享全局变量
代码实现的功能:
创建work01与worker02函数,对全局变量进行加一操作创建main函数,生成两个线程,同时调用两个函数
代码如下:
import threading result = 0 # 定义全局变量result def work1(num): global result for i in range(num): result += 1 print('------from work1-------', result) def work2(num): global result for i in range(num): result += 1 print('------from work2-------', result) def main(): print('--------begin--------', result) # 创建两个线程 t1 = threading.Thread(target=work1, args=(1000,)) t2 = threading.Thread(target=work1, args=(1000,)) t1.start() t2.start() if __name__ == '__main__': main()
运行结果:
--------begin---------- 0 ------from work1------- 1000 ------from work1------- 2000
两个线程之间共享了全局变量result,但是当我们把range的数值调大一些,
t1 = threading.Thread(target=work1, args=(1000000,)) t2 = threading.Thread(target=work1, args=(1000000,))
我们再来看一下结果,这是为什么呢,我们往下看一下结果:
--------begin---------- 0 ------from work1------- 1358452 ------from work1------- 1696352
总结:
在一个进程内的所有线程共享全局变量,很方便在多个线程间共享数据
缺点就是,线程是对全局变量随意遂改可能造成多线程之间对全局变量的混乱(即线程非安全)
二、给线程加一把锁锁
假如当前 g_num 值是100,当线程1执行第一步时,cpu通过计算获得结果101,并准备把计算的结果101赋值给g_num,然后再传值的过程中,线程2突然开始执行了并且执行了第一步,此时g_num的值仍未100,101还在传递的过程中,还没成功赋值,线程2获得计算结果101,并准备传递给g_num,经过一来一去这么一折腾,分明做了两次加 1 操作,g_num结果却是101,误差就由此产生,往往循环次数越多,产生的误差就越大,此时我们可以加一把锁。
acquire() — 锁定资源,此时资源是锁定状态,其他线程无法修改锁定的资源,直到等待锁定的资源释放之后才能操作;
release() — 释放资源,也称为解锁操作,对锁定的资源解锁,解锁之后其他线程可以对资源正常操作;
以上面的代码为列子:想得到正确的结果,可以直接利用互斥锁在全局变量 加1 之前 锁定资源,然后在计算完成之后释放资源,这样就是一个完整的计算过程,至于应该是哪个线程先执行,无所谓,先到先得,凭本事说话….演示代码如下:
# 开发时间:2022-01-27 12:59 import threading result = 0 # 定义全局变量result mutex = threading.Lock() def work1(num): global result mutex.acquire() for i in range(num): result += 1 print('------from work1-------', result) mutex.release() def work2(num): global result mutex.acquire() for i in range(num): result += 1 print('------from work2-------', result) mutex.release() def main(): print('--------begin----------', result) # 创建两个线程 t1 = threading.Thread(target=work1, args=(100000000,)) t2 = threading.Thread(target=work1, args=(100000000,)) t1.start() t2.start() if __name__ == '__main__': main()
我们来看一下结果:
--------begin---------- 0 ------from work1------- 100000000 ------from work1------- 200000000
三、死锁问题
1.单个互斥锁的死锁:
acquire()/release() 是成对出现的,互斥锁对资源锁定之后就一定要解锁,否则资源会一直处于锁定状态,其他线程无法修改;就好比上面的代码,任何一个线程没有释放资源release(),程序就会一直处于阻塞状态(在等待资源被释放),不信你可以试一试~
2.多个互斥锁的死锁:
在同时操作多个互斥锁的时候一定要格外小心,因为一不小心就容易进入死循环,假如有这样一个场景:boss让程序员一实现功能一的开发,让程序员二实现功能二的开发,功能开发完成之后一起整合代码!
# 导入线程threading模块 import threading # 导入线程time模块 import time # 创建互斥锁 mutex_one = threading.Lock() mutex_two = threading.Lock() def programmer_thread1(): mutex_one.acquire() print("我是程序员1,module1开发正式开始,程序一加锁,程序二加锁") time.sleep(2) # 此时会堵塞,因为这个mutex_two已经被线程programmer_thread2抢先上锁了,等待解锁 mutex_two.acquire() print("等待程序员2通知我合并代码") mutex_two.release() print('程序员2开发完了,程序员1释放第二把锁') mutex_one.release() print('程序员1开发完了,程序员1释放第一把锁') def programmer_thread2(): mutex_two.acquire() print("我是程序员2,module2开发正式开始,程序二加锁,程序一加锁") time.sleep(2) # 此时会堵塞,因为这个mutex_one已经被线程programmer_thread1抢先上锁了,等待解锁 #mutex_two.release() mutex_one.acquire() print("等待程序员1通知我合并代码") mutex_one.release() print('程序员2释放第一把锁') # mutex_two.release() print('程序员2释放第二把锁') def main(): t1 = threading.Thread(target=programmer_thread1) t2 = threading.Thread(target=programmer_thread2) # 启动线程 t1.start() t2.start() # 阻塞函数,等待线程结束 t1.join() t2.join() # 整合代码结束 print("整合代码结束 ") if __name__ == "__main__": main()
分析下上面代码:程序员1在等程序员2通知,程序员2在等程序员1通知,两个线程都陷入阻塞中,因为两个线程都在等待对方解锁,这就是死锁!所以在开发中对于死锁的问题还是需要多多注意!
总结
加载全部内容