Python多线程基础:变量共享、资源竞争与互斥锁实战

小林子
小林子
发布时间:2026-10-13 | 4人浏览过
原创

本文深入解析Python多线程编程中的核心问题。从基础线程创建入手,探讨全局变量在线程间的共享机制,分析因CPU调度导致的资源竞争现象。通过引入互斥锁解决数据不一致问题,并演示死锁成因及银行家算法等预防策略,帮助开发者掌握并发编程的关键技巧。

Python多线程基础示例

1、demo

首先展示多线程的基础用法,包括如何通过继承Thread类或定义目标函数来创建和启动线程。


import threading
import time
class ClassName(threading.Thread):
	"""创建类,通过多线程执行"""
	def run(self):
		for i in range(5):
			print(i)
			time.sleep(1)

def sing():
	for i in range(1,11):
		print("唱歌第 %d 遍" % i)
		time.sleep(1)

def dance():
	for i in range(1,16):
		print("跳舞第 %d 遍" % i)
		time.sleep(1)

def main():
	t1 = threading.Thread(target = sing)
	t2 = threading.Thread(target = dance)
	t = ClassName()
	
	# 启动线程
	t1.start()
	t2.start()
	t.start()

	while True:
		length = len(threading.enumerate())
		print("正在运行的线程有 %s" %threading.enumerate())
	
		if length <= 1:
			break
		time.sleep(1)

if __name__ == '__main__':
	main()

运行上述代码后,可以观察到函数sing、dance以及自定义类中的逻辑是并发执行的。由于输出内容较多,此处省略运行截图。

多线程中的变量共享机制

2、多线程共享变量

接下来演示线程间如何共享数据。通过定义一个全局变量,在test1函数内部修改该全局变量,而在test2函数中读取并打印该全局变量。


import threading
import time

#定义全局变量
g_num = 0

def test1():
	"""函数test1对全局变量进行更改"""
	global g_num
	for i in range(1,10):
		g_num += 1

	print("--- test1 线程 g_num = %d--- " % g_num)

def test2():
	"""函数test2 打印全局变量"""
	print("--- test2 线程 g_num = %d--- " % g_num)

def main():
	t1 = threading.Thread(target=test1)
	t2 = threading.Thread(target=test2)

	# 启动线程
	t1.start()
	# 增加睡眠是为了保证优先执行函数test1
	time.sleep(1)
	t2.start()

	print("--- 主线程 g_num = %d--- " % g_num)

if __name__ == '__main__':
	main()

从执行结果可以看出,主线程以及创建的两个子线程读取到的全局变量值是相同的。这证明了在Python多线程环境中,全局变量是默认共享的。

资源竞争问题分析

3、资源竞争

当多个线程同时尝试修改同一个变量时,可能会引发资源竞争问题。由于CPU的执行调度机制,如果修改参数的代码块不能作为一个原子操作一次性完成,就会导致数据不一致。


import threading
import time

# 定义全局变量
g_num = 0

def test1(num):
	"""函数test1对全局变量进行更改"""
	global g_num
	for i in range(num):
		g_num += 1

	print("test1 线程 g_num = %d---" % g_num)

def test2(num):
	"""函数test2对全局变量进行更改"""
	global g_num
	for i in range(num):
		g_num += 1

	print("tes2 线程 g_num = %d---" % g_num)

def main():
	t1 = threading.Thread(target=test1, args=(1000000, ))
	t2 = threading.Thread(target=test2, args=(1000000, ))

	t1.start()
	t2.start()

	time.sleep(1)
	print("主线程 g_num = %d---" % g_num)

if __name__ == '__main__':
	main()

我们可以尝试将参数设置为100,此时观察到g_num的结果为200。这是因为在参数较小时,函数代码可能恰好能一次性执行完成。然而,当参数增大到1000000时,代码无法一次性执行完毕,此时g_num的值将不再等于2000000,体现了资源竞争带来的影响。

使用互斥锁解决竞争问题

4、互斥锁

为了解决资源竞争问题,可以使用互斥锁(Mutex)。其原理非常直观:通过对关键代码段加锁,确保在该代码执行完成前,其他线程无法对其进行修改。执行完毕后解锁,其他线程即可继续执行。


import threading
import time

# 创建变量
g_num = 0
# 创建锁默认为开锁状态
mutex = threading.Lock()

def test1(num):
	global g_num
	for i in range(num):
		# 上锁
		mutex.acquire()
		g_num += 1
		# 解锁
		mutex.release()
	print("--- test1 线程 g_num = %d---" % g_num)

def test2(num):
	global g_num
	for i in range(num):
		# 上锁
		mutex.acquire()
		g_num += 1
		# 解锁
		mutex.release()

	print("--- test2 线程 g_num = %d---" % g_num)

def main():
	t1 = threading.Thread(target=test1, args=(1000000, ))
	t2 = threading.Thread(target=test2, args=(1000000, ))

	t1.start()
	t2.start()

	time.sleep(1)
	print("--- 主线程 g_num = %d---" % g_num)

if __name__ == '__main__':
	main()

加入锁机制后,代码执行不再出现资源竞争,最终结果也符合预期。需要注意的是,加锁的范围应尽可能小,以减少对性能的影响。

死锁成因及预防策略

5、死锁

当程序中存在多个锁时,可能会发生死锁现象。如果尝试关闭一个已经处于关闭状态的锁,或者多个线程互相等待对方释放锁,程序就会陷入阻塞。如下方代码所示,当函数test1尝试关闭mutexB锁时,如果函数test2已经提前将其关闭且未解锁,程序就会一直阻塞。


import threading
import time

# 创建两个锁A, B
mutexA = threading.Lock()
mutexB = threading.Lock()

def test1():
	# 对muctexA上锁
	mutexA.acquire()

	# mutexA上锁后,延时1秒,等待mutexB上锁
	print("test1 ---do1---up---")
	time.sleep(1)
	# 此时会堵塞,因为mutexB已经上锁
	mutexB.acquire()
	print("test1 ---do1---down---")
	mutexB.release()

	# 对mutexA解锁
	mutexA.release()

def test2():
	# 对muctexB上锁
	mutexB.acquire()

	# mutexB上锁后,延时1秒,等待mutexA上锁
	print("test2 ---do1---up---")
	time.sleep(1)
	# 此时会堵塞,因为mutexB已经上锁
	mutexA.acquire()
	print("test2 ---do1---down---")
	mutexA.release()

	# 对mutexA解锁
	mutexB.release()

def main():
	t1 = threading.Thread(target=test1)
	t2 = threading.Thread(target=test2)

	t1.start()
	t2.start()


if __name__ == '__main__':
	main()

从代码执行效果可以看到,程序会一直阻塞无法结束。 解决方法 1、在程序编写阶段就需仔细设计,避免产生死锁 2、可以参考银行家算法来预防死锁

至此,关于Python多线程及变量共享问题的介绍结束。更多相关Python多线程变量共享内容,请搜索本站以前的文章或继续浏览下面的相关文章,希望大家以后多多支持本站!

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表