前言:
今天大家对“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线程全局变量