在多线程中,如果多线程同时对一个全局变量进行操作时,就有可能出现异常,比如下面的程序:
# -*- coding:UTF-8 -*-import threadingimport time#定义一个全局变量g_num = 0def test1(num): global g_num for i in range(num): g_num += 1 print(g_num)def test2(num): global g_num for i in range(num): g_num += 1 print(g_num)def main(): t1 = threading.Thread(target=test1,args=(10000000,)) t2 = threading.Thread(target=test2,args=(10000000,)) t1.start() t2.start() time.sleep(5) print('结束:',g_num)if __name__ == '__main__': main()
运行后得到的结果本应是20000000,可是结果真是这样吗?运行后得到的结果是:
这是因为一个程序在未运行完全时就被休眠而执行另一个程序了,从而导致这样的结果。说明多个线程同时对一个全局变量进行操作,会出现资源竞争问题,从而数据结果会不正确,导致线程安全问题。简单来说:
在g_num=0时,t1取得g_num=0。此时系统把t1调度为”sleeping”状态,把t2转换为”running”状态,t2也获得g_num=0
然后t2对得到的值进行加1并赋给g_num,使得g_num=1
然后系统又把t2调度为”sleeping”,把t1转为”running”。线程t1又把它之前得到的0加1后赋值给g_num。
这样导致虽然t1和t2都对g_num加1,但结果仍然是g_num=1
那么怎么解决这个问题呢?这里就用到了多线程的互斥锁。具体参见下面的程序:
# -*- coding:UTF-8 -*-import threadingimport time#定义一个全局变量g_num = 0def test1(num): global g_num #上锁 mutex.acquire() for i in range(num): g_num += 1 #解锁 mutex.release() print(g_num)def test2(num): global g_num # 上锁 mutex.acquire() for i in range(num): g_num += 1 mutex.release() print(g_num)def main(): t1 = threading.Thread(target=test1,args=(10000000,)) t2 = threading.Thread(target=test2,args=(10000000,)) t1.start() t2.start() time.sleep(3) print('结束:',g_num)if __name__ == '__main__': mutex = threading.Lock() main()
互斥锁
当多个线程几乎同时修改共享数据时,需要进行同步控制
线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制就是引入互斥锁。
互斥锁为我们的资源引入一个状态:锁定/非锁定
某个线程要更改共享数据时,先将其锁定,此时,资源的状态为锁定,其他线程不能对其更改;
直到该线程释放资源,资源状态变为非锁定状态,其他线程才能再次锁定该资源。
互斥锁,保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。
在threading模块里,定义了Lock()类,可以方便的处理锁定。
此时我们就可以得到我们想要的答案了:
END