一. Python线程互斥锁Lock
使用多线程可以同时执行多个任务,提高开发效率,但是在实际开发中往往我们会碰到线程同步问题,假如有这样一个场景:对全局变量累加1000000此,为了提高开发效率,我们可以使用多线程完成,示例代码如下:
#!/usr/bin/python # -*- coding: utf-8 -*- # 导入线程threading模块 import threading # 声明全局变量 g_num = 0 def my_thread1(): # 声明全局变量 global g_num # 循环 1000000 次,每次累计加 1 for i in range(0, 1000000): g_num = g_num + 1 def my_thread2(): # 声明全局变量 global g_num # 循环 1000000 次,每次累计加 1 for i in range(0, 1000000): g_num = g_num + 1 def main(): # 声明全局变量 global g_num # 初始化全局变量,初始值为 0 # 创建两个线程,对全局变量进行累计加 1 t1 = threading.Thread(target=my_thread1) t2 = threading.Thread(target=my_thread2) # 启动线程 t1.start() t2.start() # 阻塞函数,等待线程结束 t1.join() t2.join() # 获取全局变量的值 print("计算结果:%d " % (g_num)) if __name__ == "__main__": main()
执行结果:
这个是什么操作?看着代码好像也没问题,两个线程,各自累加1000000次,不应该输出的是2000000吗?
1. 线程共享全局变量
分析上面的代码:两个线程共享全局变量并执行for循环1000000,每次自动加1,我们都知道两个线程都是在同时运行,也就是说两个线程同时在执行g_num = g-num = 1操作,经过我们冷静分析一波,貌似结果该是应该等于2000000,对不对?
首先我们将上面的全局变量自动加1的代码分为两步:
第一步: g_num + 1
第二步: 将g_num + 1的结果赋值给g_num
由此可见,执行一个完整的自动加1的过程需要两步,然而线程确是同时在运行,谁也不能保证线程1的第一步和第二部执行完成之后才执行线程2的第一步和第二步,执行的过程充满随机性,这就是导致每次计算结果不同的原因所在。
2. 线程互斥锁
为了避免上面的问题,我们可以利用线程互斥锁解决这个问题,那么线程互斥锁到底是个什么原理呢?互斥锁就好比排队上厕所,一个坑位只能蹲一个人,只有占用坑位的人完事了,另一个人才能上!!
创建互斥锁
导入线程模块,通过threading.Lock()创建互斥锁
import threading
mutex = threading.Lock()
锁定资源/解锁资源
acquire() --锁定资源,此时资源是锁定状态,其他线程无法修改锁定的资源,知道等待锁定的资源释放之后才能操作
release() --释放资源,也成为解锁操作,对锁定的资源解锁,解锁之后其他线程可以对资源正常操作
上面的代码为例子:想得到正确的结果,可以直接利用互斥锁在全局变量加1之前锁定资源,然后在计算完成之后释放资源,这样就是一个完整的计算过程,至于应该是那个线程先执行,无所谓,先到先得,凭本事说话
#!/usr/bin/python # -*- coding: utf-8 -*- # 导入线程threading模块 import threading mutex = threading.Lock() # 声明全局变量 g_num = 0 def my_thread1(): # 声明全局变量 global g_num # 循环 1000000 次,每次累计加 1 for i in range(0, 1000000): mutex.acquire() g_num = g_num + 1 mutex.release() def my_thread2(): # 声明全局变量 global g_num # 循环 1000000 次,每次累计加 1 for i in range(0, 1000000): mutex.acquire() g_num = g_num + 1 mutex.release() def main(): # 声明全局变量 global g_num # 初始化全局变量,初始值为 0 # 创建两个线程,对全局变量进行累计加 1 t1 = threading.Thread(target=my_thread1) t2 = threading.Thread(target=my_thread2) # 启动线程 t1.start() t2.start() # 阻塞函数,等待线程结束 t1.join() t2.join() # 获取全局变量的值 print("计算结果:%d " % (g_num)) if __name__ == "__main__": main()
执行结果: