# -*- coding=utf-8 -*-
# Author: BoLin Chen
# @Date : 2021-02-22
import threading, time
num = 0
def th1(temp):
global num
for i in range(temp):
num += 1
print(f"----------in the th1 num is : {num}----------")
def th2(temp):
global num
for i in range(temp):
num += 1
print(f"----------in the th2 num is : {num}----------")
def main():
t1 = threading.Thread(target=th1, args=(1000000,))
t2 = threading.Thread(target=th2, args=(1000000,))
t1.start()
# time.sleep(1)
t2.start()
time.sleep(3)
print(f"------- ---in the main num is : {num}----------")
if __name__ == '__main__':
main()
运行结果如下:
----------in the th1 num is : 1165597----------
----------in the th2 num is : 1262044----------
------- ---in the main num is : 1262044----------
经过分析发下,当使用两个线程对全局变量(初始值为0)各进行100万次的 +1 操作,最终结果却不是 200万,但是把增加的次数变为 10、100、1000、10000、10000,结果都是和预期符合的20、200、2000、20000、200000,这里其实就是CPU在调用多线程的逻辑,每次调用单个线程,并不是一定把单个线程的这段代码运行完毕才切换到另一个线程,可能这一段代码运行到一半就切换到另一个线程去了,导致两个线程 +1 的操作都是基于某个数字+1,但是当前面运行到一半的线程切换回来运行完后,此时的数字已经+1,但是后面的线程的后一半代码还是基于之前的数字,所以这次 +1 的操作就没有实际意义,导致两次+1原先应该+2 的操作就只出现了+1;
如果我们控制某个线程在执行对全局数据修改时的操作不进行切换,修改完后再切换到另一个线程不就可以了吗?所以这时候就出现了互斥锁。
某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。
threading 模块中定义了 lock 类,可以方便处理锁定;
# 创建锁
mutex = threading.Lock()
# 锁定
mutex.acquire()
# 释放
mutex.release()
注意
acquire
不会堵塞acquire
对这个锁上锁之前它已经被其他线程上了锁,那么此时 acquire
会堵塞,直到这个锁被解锁为止。import threading, time
# 使用互斥锁完成2个线程对同一个全局变量各加100万次的操作
num = 0
mutex = threading.Lock() # 创建一个互斥锁
def th1(temp):
global num
mutex.acquire()
for i in range(temp):
num += 1
mutex.release()
print(f"----------in the th1 num is : {num}----------")
def th2(temp):
global num
mutex.acquire()
for i in range(temp):
num += 1
mutex.release()
print(f"----------in the th2 num is : {num}----------")
def main():
t1 = threading.Thread(target=th1, args=(1000000,))
t2 = threading.Thread(target=th2, args=(1000000,))
t1.start()
# time.sleep(1)
t2.start()
# time.sleep(3)
print(f"----------in the main num is : {num}----------")
if __name__ == '__main__':
main()
运行结果如下:
----------in the main num is : 260187----------
----------in the th1 num is : 1000000----------
----------in the th2 num is : 2000000----------
这里可以看到,如果互斥锁加在 for 循环外面,那么一定会等到一个线程执行完 for 循环才会切换到另一个线程,所以第一个 num 打印的值一定是 1000000;
尝试变更互斥锁的位置:
num = 0
mutex = threading.Lock() # 创建一个互斥锁
def th1(temp):
global num
for i in range(temp):
mutex.acquire()
num += 1
mutex.release()
print(f"----------in the th1 num is : {num}----------")
def th2(temp):
global num
for i in range(temp):
mutex.acquire()
num += 1
mutex.release()
print(f"----------in the th2 num is : {num}----------")
def main():
t1 = threading.Thread(target=th1, args=(1000000,))
t2 = threading.Thread(target=th2, args=(1000000,))
t1.start()
# time.sleep(1)
t2.start()
# time.sleep(3)
print(f"----------in the main num is : {num}----------")
if __name__ == '__main__':
main()
运行结果如下:
----------in the main num is : 44445----------
----------in the th1 num is : 1936222----------
----------in the th2 num is : 2000000----------
这时可以看出,th1 中的 num 其实是在第一个线程执行 for 循环时候,两个线程对 num 共同+1 的结果,最后的 num 结果则是后执行完的线程最后一次 +1 的操作的结果,由于代码中加了互斥锁,每次每个线程对 num 变量进行操作都是有意义的,最终 num 被操作200万次;