龙空技术网

多线程操作全局变量,小心掉坑里了

l想去有风的地方 149

前言:

今天大家对“python 多线程 全局变量”可能比较关注,兄弟们都想要分析一些“python 多线程 全局变量”的相关知识。那么小编在网络上网罗了一些有关“python 多线程 全局变量””的相关知识,希望我们能喜欢,朋友们快快来学习一下吧!

前面有介绍线程可以共享全局变量,那时候,一个线程修改,一个线程访问,似乎用的很顺畅,但是很多时候,可能多个线程都会对这个全局变量进行操作,那么,不做处理会依然正常吗?当然不会,肯定会出问题了。

问题背景

多线程开发可能遇到的问题

假设两个线程t1和2都要对全局变量num(默认是0)j进行加1运算,t1和t2都各对num加10次。num的最终的结果应该为20。但是由于是多线程同时操作,有可能出现下面的情况:

1、在num=0时t1取得num=0,此时系统把t1调度为sleeping状态,把t2转换为running状态,

t2也获得num=0。

2、然后t2对得到的值进行加1并赋给num使得num=1。

3、然后系统又把2调度为 sleeping,把t1转为running,线程t1又把它之前得到的0加1后赋值给num。

4、这样导致t1和t2都对num加1,但结果仍然是num=1。

#!/usr/bin/env  pythonimport threading, timenum = 0def test1(count):     global num     for i in range(count):         num += 1     print("-----test1-----中num最后的值是:%d" % num)def test2(count):    global num    for i in range(count):        num += 1    print("-----test2------中num最后的值是:%d" % num)if __name__ == '__main__':    t1 = threading.Thread(target=test1, args=(100,))    t2 = threading.Thread(target=test2, args=(100,))    t1.start()    t2.start()    time.sleep(1)    print("-----main------中num最后的值是:%d" % num)

似乎没有出问题呀!

那如果对这个变量操作的次数再多的时候,情况又是如何呢?

当我们把上面主程序中的函数执行次数变一下

if __name__ == '__main__':    t1 = threading.Thread(target=test1, args=(10000000,))    t2 = threading.Thread(target=test2, args=(10000000,))    t1.start()    t2.start()    time.sleep(3)    print("-----main------中num最后的值是:%d" % num)

what?什么鬼?

不应该结果是20000000嘛。

原因分析

为什么会出现这样的情况呢?

线程在执行的时候,也是分很多步来操作的,虽然cpu的执行速度非常快我们无法感知,但是,当线程多步在分别执行的时候,问题就出现了。

以上的问题其实函数处理包含了三步,一是获取num值,二是对num值加1,三是加1的结果给到num。

那么问题就在这里,当线程1在执行这三步的任何一步的时候,线程2可能也在执行。当线程1执行到num+1的时候,本来num值要加1重新赋值了。线程2开始读原始值,准备加1。线程1做完了结果变成1,恰哈线程2读取了上一步的值,num=0。线程1和线程2发生冲突了。

基本解决办法

sleep当然可以解决,但是就违背了多任务的初衷。

线程自身就提供了一种方法,上锁机制。

线程1在处理的时候,对变量上一个锁,处理完再把变量释放出来。同样,线程2在操作的时候也要上锁再解锁。

这么简单?那上个锁试试。

互斥锁

当多个线程几乎同时修改某一个共享数据的时候需要进行同步控制。

线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引互斥锁。

互斥锁为资源引入一态:锁定/非锁定。

某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁”,其他线程不能更改直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源,互锁保证了每次只有一个线程。

进行写入操作,从而保证了多线程情况下数据的正确性。

创建锁

mutex = threading.Lock()

上锁

mutex.acquire()

释放锁

mutex.release()

同一个锁一次只能有一个人上锁,另一个阻塞等待锁释放。

第一次尝试

#!/usr/bin/env  pythonimport threading, timenum = 0def test1(count):     global num     mutex.acquire()     for i in range(count):         num += 1     mutex.release()     print("-----test1-----中num最后的值是:%d" % num)def test2(count):    global num    mutex.acquire()    for i in range(count):        num += 1    mutex.release()    print("-----test2------中num最后的值是:%d" % num)if __name__ == '__main__':    mutex = threading.Lock()    t1 = threading.Thread(target=test1, args=(10000000,))    t2 = threading.Thread(target=test2, args=(10000000,))    t1.start()    t2.start()    time.sleep(3)    print("-----main------中num最后的值是:%d" % num)

貌似也能实现,但是感觉这样的话,似乎又回到了单人舞一个一个执行的节奏。因为这样上锁在循环之外的话,相当于一个结果执行完,才会去执行另一个函数。这样肯定是不能满意的。

那接着来第二次尝试

#!/usr/bin/env  pythonimport threading, timenum = 0def test1(count):     global num     #修改点     for i in range(count):         mutex.acquire()         num += 1         mutex.release()     print("-----test1-----中num最后的值是:%d" % num)def test2(count):    global num    for i in range(count):        mutex.acquire()        num += 1        mutex.release()    print("-----test2------中num最后的值是:%d" % num)if __name__ == '__main__':    mutex = threading.Lock()    t1 = threading.Thread(target=test1, args=(10000000,))    t2 = threading.Thread(target=test2, args=(10000000,))    t1.start()    t2.start()    time.sleep(10)    print("-----main------中num最后的值是:%d" % num)

结果也是可以的,这样的才是多任务的并发执行的节奏。这样一来,也就简单地解决了共享变量在线程中同时修改出错的问题。

那么问题就解决了吗?并没有。互斥锁还是需要优化的。为什么要优化,怎么优化?下次聊。

标签: #python 多线程 全局变量 #python线程全局变量