Python線程之認(rèn)識線程安全?
一、什么是線程安全?
線程安全,名字就非常直接,在多線程情況下是安全的,多線程操作上的安全。
比如一個計算加法的函數(shù),不管是一千個還是一萬個線程,我們希望它執(zhí)行的結(jié)果總是正確的,1+1 必須永遠(yuǎn)等于2, 而不是線程少的時候1+1 變成3或者4了。
通常我們都用線程安全來修飾一個類,修飾一個函數(shù):
我們會說我設(shè)計的這個類是線程安全的
這意味著,在多線程環(huán)境下,同時調(diào)用這個類的函數(shù)不會出現(xiàn)函數(shù)設(shè)置預(yù)期之外的異常(上述的1+1=3的情況)
二、在Python中有哪些類是線程安全的?
dict 和 list,tuple這些都是線程安全。
它們是被全局解釋器保障了,這個鎖:GIL(全局解釋器鎖)確保了任何時候只能有一個線程執(zhí)行相應(yīng)操作的字節(jié)碼。參考

但是這番話也是說的不清不楚的。
現(xiàn)在我們拿轉(zhuǎn)賬來解析吧:
xuewei_account = dict()
xuewei_account['amount'] = 100
# amount為負(fù)數(shù)即是轉(zhuǎn)出金額
def transfer(money):
xuewei_account['amount'] += money如上,代碼為一個函數(shù)對jb_account(賬戶)進(jìn)行轉(zhuǎn)入金額操作。
這里用了dict類型,GIL會保證只有一個線程操作賬戶。
下面是多個線程進(jìn)行操作的代碼:
import random
import threading
import datetime
import time
xuewei_account = dict()
xuewei_account['amount'] = 100
# amount為負(fù)數(shù)即是轉(zhuǎn)出金額
def transfer(money):
xuewei_account['amount'] += money
# 創(chuàng)建4個任務(wù)給重復(fù)學(xué)委賬戶轉(zhuǎn)賬
threads = []
for i in range(200):
t1 = threading.Thread(target=lambda: transfer(-1))
threads.append(t1)
t2 = threading.Thread(target=lambda: transfer(1))
threads.append(t2)
for t in threads:
t.start()
# 這次不用sleep了,用join來等待所有線程執(zhí)行完畢
# join函數(shù)必須線程start后才能調(diào)用,否則出錯。
for t in threads:
t.join()
print("-" * 16)
print("活躍線程數(shù):", threading.active_count())
print("活躍線程:", threading.current_thread().name)
print("學(xué)委賬戶余額:", xuewei_account)
這段代碼運(yùn)行的輸出結(jié)果正常,因為是反復(fù)+1/-1,最后肯定是恢復(fù)原賬戶余額。
雖然多個線程,但是每個線程只對xuewei_account進(jìn)行一次讀寫,這時候dict是安全的。

但是我們把賦值修改dict的操作變多之后(特別是一個線程內(nèi)反復(fù)多次獲取值然后修改),像下面的代碼:
import random
import threading
import datetime
import time
xuewei_account = dict()
xuewei_account['amount'] = 100
# amount為負(fù)數(shù)即是轉(zhuǎn)出金額
def transfer(money):
? ? for i in range(100000):
? ? ? ? xuewei_account['amount'] = xuewei_account['amount'] + money
# 創(chuàng)建400個任務(wù)重復(fù)給學(xué)委賬戶轉(zhuǎn)賬
threads = []
for i in range(200):
? ? t1 = threading.Thread(target=lambda: transfer(-1))
? ? threads.append(t1)
? ? t2 = threading.Thread(target=lambda: transfer(1))
? ? threads.append(t2)
for t in threads:
? ? t.start()
for t in threads:
? ? t.join()
print("-" * 16)
print("活躍線程數(shù):", threading.active_count())
print("活躍線程:", threading.current_thread().name)
print("學(xué)委賬戶余額:", xuewei_account)這是某一次運(yùn)行結(jié)果(不保證每次acount的數(shù)值一樣):

我們看到dict還是扛不住多個線程反復(fù)的寫操作。
這里區(qū)別是:每個線程只對xuewei_account進(jìn)行大量讀寫,雖然dict是安全的,但是多個線程中間穿插修改了account,程序方法棧出現(xiàn)操作到舊值(看下面的圖)。
主要是下面這段代碼:
xuewei_account[‘a(chǎn)mount'] += money # 即是 xuewei_account[‘a(chǎn)mount'] = xuewei_account[‘a(chǎn)mount']+ money
再一步抽象簡化可以寫成:
a = a + b
每個線程都執(zhí)行 +b 操作,最后a的值應(yīng)該是a+2b。
上面的操作意味這下面的情況發(fā)生了:

在某個線程中可能出現(xiàn)某一個線程T1獲取了a值 ,準(zhǔn)備加上b。
另外一個線程T2已經(jīng)完成了a+b操作,把a(bǔ)的值變成了a+b了。
但是接下來T1 拿了a的值再執(zhí)行a+b操作,把a(bǔ)的值變成a+b。
這樣就少加了一個b,本來最后結(jié)果是a+2b 的變成了 a+b(因為T1拿了a的舊值,中間T2執(zhí)行完,T1才繼續(xù)執(zhí)行)
當(dāng)然實際多線程之間交互比上圖還要隨機(jī)。
三、如何做到真正線程安全?
dict讀取數(shù)據(jù)是線程安全,但是被反復(fù)讀寫就容易出現(xiàn)數(shù)據(jù)混亂。
如果我們要設(shè)計一個線程安全的函數(shù),那么它必須不涉及任何共享變量或者是完全沒有狀態(tài)依賴的函數(shù)
def thread_safe_method(): ? ? pass
1.無狀態(tài)函數(shù)
比如下面的加法函數(shù),不管多少個線程調(diào)用,返回值永遠(yuǎn)是預(yù)期的a+b。
def add(a, b): ? ? return a + b
2.另一種 化繁為簡
許我們可以把多線程轉(zhuǎn)換為單線程,這個需要一個線程安全的媒介。
到此這篇關(guān)于Python線程之認(rèn)識線程安全 的文章就介紹到這了,更多相關(guān)認(rèn)識Python線程安全 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python動態(tài)強(qiáng)類型解釋型語言原理解析
這篇文章主要介紹了Python動態(tài)強(qiáng)類型解釋型語言原理解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-03-03
讀取json格式為DataFrame(可轉(zhuǎn)為.csv)的實例講解
今天小編就為大家分享一篇讀取json格式為DataFrame(可轉(zhuǎn)為.csv)的實例講解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-06-06
用python統(tǒng)計代碼行的示例(包括空行和注釋)
今天小編就為大家分享一篇用python統(tǒng)計代碼行的示例(包括空行和注釋),具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-07-07
Python實現(xiàn)的查詢mysql數(shù)據(jù)庫并通過郵件發(fā)送信息功能
這篇文章主要介紹了Python實現(xiàn)的查詢mysql數(shù)據(jù)庫并通過郵件發(fā)送信息功能,可實現(xiàn)Python針對mysql的查詢與宕機(jī)信息通過郵件發(fā)送的相關(guān)操作技巧,需要的朋友可以參考下2018-05-05
利用python實現(xiàn).dcm格式圖像轉(zhuǎn)為.jpg格式
今天小編就為大家分享一篇利用python實現(xiàn).dcm格式圖像轉(zhuǎn)為.jpg格式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-01-01
解決python多行注釋引發(fā)縮進(jìn)錯誤的問題
今天小編就為大家分享一篇解決python多行注釋引發(fā)縮進(jìn)錯誤的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-08-08

