Python线程中的互斥锁

互斥锁引入背景:

# -*- 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万次;

你可能感兴趣的:(Python,多线程,互斥锁,thread.Lock,python,并发编程)