例:假設(shè)兩個(gè)線程t1和t2都要對(duì)num=0進(jìn)行增1運(yùn)算愕把,t1和t2都各對(duì)num修改10次拣凹,num的最終的結(jié)果應(yīng)該為20。
但是由于是多線程訪問礼华,有可能出現(xiàn)下面情況:
在num=0時(shí)咐鹤,t1取得num=0。此時(shí)系統(tǒng)把t1調(diào)度為”sleeping”狀態(tài)圣絮,把t2轉(zhuǎn)換為”running”狀態(tài)祈惶,t2也獲得num=0。
然后t2對(duì)得到的值進(jìn)行加1并賦給num扮匠,使得num=1捧请。然后系統(tǒng)又把t2調(diào)度為”sleeping”,把t1轉(zhuǎn)為”running”棒搜。
線程t1又把它之前得到的0加1后賦值給num疹蛉。這樣,明明t1和t2都完成了1次加1工作力麸,但結(jié)果仍然是num=1
1.g_num的值大部分是在一百萬到兩百萬之間的情況
from threading import Thread
g_num = 0
def test1():
global g_num
for i in range(1000000):
# g_num += 1
b = g_num + 1
g_num = b
print("---test1---g_num=%d"%g_num)
def test2():
global g_num
for i in range(1000000):
a = g_num + 1
g_num = a
print("---test2---g_num=%d"%g_num)
if __name__ == '__main__':
p1 = Thread(target=test1)
p1.start()
p2 = Thread(target=test2)
p2.start()
#--------------------運(yùn)行結(jié)果------------------------
---test1---g_num=1283303
---test2---g_num=1257613
Process finished with exit code 0
以上結(jié)果多個(gè)線程對(duì)同一資源的訪問可款,對(duì)數(shù)據(jù)造成破壞,使得線程運(yùn)行的結(jié)果不可預(yù)期克蚂。這種現(xiàn)象稱為“線程不安全”
2.g_num的值小于一百萬的情況
from threading import Thread
import time
g_num = 0
def test1():
global g_num
for i in range(1000000):
# g_num += 1
b = g_num + 1
time.sleep(0.1) #此處添加阻塞狀態(tài)闺鲸,是為了讓b結(jié)果沒有賦值給g_num就被切換到另一個(gè)函數(shù)
g_num = b
print("---test1---g_num=%d"%g_num)
def test2():
global g_num
for i in range(1000000):
a = g_num + 1
g_num = a
print("---test2---g_num=%d"%g_num)
if __name__ == '__main__':
p1 = Thread(target=test1)
p1.start()
p2 = Thread(target=test2)
p2.start()
#--------------------運(yùn)行結(jié)果------------------------
---test2---g_num=27712
注:此結(jié)果沒有出現(xiàn) Process finished這句話的原因是sleep 0.1 秒 還要運(yùn)行幾十萬次以上,
時(shí)間太長(zhǎng)埃叭,所以程序 test1 其實(shí)一直在運(yùn)行摸恍,只能強(qiáng)制停止程序。
跟第一個(gè)例子一樣,線程不安全
3.正常情況g_num
from threading import Thread
import time
g_num = 0
def test1():
global g_num
for i in range(1000000):
# g_num += 1
b = g_num + 1
g_num = b
print("---test1---g_num=%d"%g_num)
def test2():
global g_num
for i in range(1000000):
a = g_num + 1
g_num = a
print("---test2---g_num=%d"%g_num)
if __name__ == '__main__':
p1 = Thread(target=test1)
p1.start()
time.sleep(1) #此處阻塞1秒立镶,給test1充分運(yùn)行時(shí)間壁袄,直至該線程運(yùn)行結(jié)束
p2 = Thread(target=test2)
p2.start()
#--------------------運(yùn)行結(jié)果------------------------
---test1---g_num=1000000
---test2---g_num=2000000
Process finished with exit code 0
本應(yīng)該輸出的g_num正確值
4.解決辦法
from threading import Thread,Lock
g_num = 0
my_lock = Lock()
def test1():
global g_num
if my_lock.acquire(): #同步鎖防止?fàn)帄Z資源
for i in range(1000000):
g_num = g_num+1
my_lock.release() #解鎖
print("---test1---g_num=%d"%g_num)
def test2():
global g_num
if my_lock.acquire():
for i in range(1000000):
g_num = g_num+1
my_lock.release()
print("---test2---g_num=%d"%g_num)
if __name__ == '__main__':
p1 = Thread(target=test1)
p1.start()
p2 = Thread(target=test2)
p2.start()
#--------------------運(yùn)行結(jié)果------------------------
---test1---g_num=1000000
---test2---g_num=2000000