共享全局变量的问题
问题来源:多线程开发的时候共享全局变量会带来资源竞争效果,也就是数据不安全。
代码示例:
from threading import Thread
import time
g_num=0
def work1(num):
global g_num
for i in range(num):
g_num +=1
print("in work1-->",g_num)
def work2(num):
global g_num
for i in range(num):
g_num +=1
print("in work2-->",g_num)
def main():
t1 = Thread(target=work1,args=(1000000,))
t2 = Thread(target=work2,args=(1000000,))
t1.start()
t2.start()
'''
运行结果:
in work1--> 1111286
in work2--> 1196255
sum-->> 1196255
'''
分析:t1线程对g_num进行一百万次加一,t2线程对g_num进行一百万次加一,我们预期最后的结果应该是二百万。但是看到运行的结果却不是我们预期的值,这是为什么了?
答:这是因为g_num += 1在执行的时候会解析成,先获取g_num的值,再进行加一,最后才是保存到g_num中。那么我们可以模拟一下cpu执行。
可能会出现以下的错误情况!
答:当work1先获取g_num的值等于0,调度系统将work1调为睡眠,切换到work2,先获取g_num的值也等于0,于是进行加一,g_num变为1,work2又睡眠,切换到work1,进行增一,但是这时候是0+1变为1。而再切换到work2,获取却是1了,将之前值覆盖,导致最后的结果不足预期的结果,这就是主要原因。
那么了解了问题所在,那有什么办法解决呢?
互斥锁
什么是互斥锁?
当多个线程几乎同时修改一个共享数据的时候,需要进行同步控制,线程同步能够保证多个 线程安全的访问竞争资源(全局内容),最简单的同步机制就是使用互斥锁。 某个线程要更改共享数据时,先将其锁定,此时资源的状态为锁定状态,其他线程就不能更 改,直到该线程将资源状态改为非锁定状态,也就是释放资源,其他的线程才能再次锁定资 源。互斥锁保证了每一次只有一个线程进入写入操作。从而保证了多线程下数据的安全性。
代码示例:
from threading import Thread
from threading import Lock
g_num=0
#创建一个全局锁对象’
lock=Lock()
def work1(num):
global g_num
lock.acquire() #加锁
for i in range(num):
g_num+=1
#解锁
lock.release()
print("in work1-->",g_num)
def work2(num):
global g_num
lock.acquire() #加锁
for i in range(num):
g_num+=1
#解锁
lock.release()
print("in work2-->",g_num)
def main():
t1 = Thread(target=work1,args=(1000000,))
t2 = Thread(target=work2,args=(1000000,))
t1.start()
t2.start()
t2.join()
if __name__ == '__main__':
main()
print("main--》", g_num)
'''
运行结果:
in work1--> 1000000
in work2--> 2000000
main--》 2000000
'''
总结:通过使用互斥锁解决共享线程资源不安全的问题的实质就是当线程操作资源的时候,将资源锁住,操作结束后,解锁。