Python多线程共享变量与锁机制详解
本文详解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
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









