Python多线程共享变量与锁机制详解

Aria
Aria
发布时间:2026-10-13 | 6人浏览过
原创

本文详解Python多线程中共享变量引发的数据竞争问题,通过加减法示例展示非原子操作导致的错误结果。重点讲解使用threading.Lock进行上锁与释放的操作流程,确保资源访问的原子性,并对比线程安全与不安全的容器类型,提供完整代码参考。

共享变量与线程安全问题

一、共享变量

当多个线程同时访问同一个变量时,就会引发共享变量相关的问题。

具体示例如下:


import threading
sum = 0
loopSum = 1000000
def myAdd():
  global sum, loopSum
for i in range(1, loopSum):
  sum += 1
def myMinu():
  global sum, loopSum
for i in range(1, loopSum):
  sum -= 1
if __name__ == "__main__":
  print("Dont,,,,,,,{0}".format(sum))
t1 = threading.Thread(target = myAdd, args = ())
t2 = threading.Thread(target = myMinu, args = ())
t1.start()
t2.start()
t1.join()
t2.join()
print("Done,,,,,,{0}".format(sum))

从上述运行结果可以看出,最终数值并非预期的0,而是-286705。这是因为多个线程同时对共享变量进行了操作,而该操作并非原子性的,导致数据出现偏差。

2.解决方案:使用“锁”或“信号灯”

(1)锁(Lock):这是一种标志,用于表示某个线程正在占用特定资源。

使用方式为:先获取锁(上锁),然后使用共享资源,安全地执行操作,最后释放锁,即释放对该变量的占用。

锁定对象:任何需要被共享访问的资源,都应当对其加锁。

使用Lock锁解决并发冲突


import threading
sum = 0
loopSum = 1000000
lock = threading.Lock()
# 先生成一个锁的实例
def myAdd():
  global sum, loopSum
for i in range(1, loopSum):
  lock.acquire()# 这里申请了一把锁
sum += 1
lock.release()# 注意千万不要忘了释放锁
def myMinu():
  global sum, loopSum
for i in range(1, loopSum):
  lock.acquire()
sum -= 1
lock.release()
if __name__ == "__main__":
  print("Done,,,,,,,{0}".format(sum))
t1 = threading.Thread(target = myAdd, args = ())
t2 = threading.Thread(target = myMinu, args = ())
t1.start()
t2.start()
t1.join()
t2.join()
print("Done,,,,,,{0}".format(sum))

正如我们预料的,加减操作的顺序本身不影响最终结果(应为零和游戏),但在上述例子中,由于存在同时对变量内存地址进行读写操作的情况,导致内存被错误覆盖,因此最终结果不符合预期。

(2)线程安全问题:

如果一个资源或变量在多线程环境下,即使不加锁也不会引发任何问题,则称其为线程安全。常见的线程不安全变量类型包括list、set和dict;而线程安全的变量类型为queue。

二、源码

d25_1_shared_variable_and_lock.py

https://github.com/ruigege66/Python_learning/blob/master/d25_1_shared_variable_and_lock.py

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。

本站声明

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

Python 相关产品和应用

全部下载

专题列表