正式的Python專欄第44篇,同學站住,別錯過這個從0開始的文章!
前面學委分享了5篇多執行緒的文章了,一開始寫多執行緒程式好像非常簡單,可是實際應用跟第4篇,第5篇的場景比較像,而且還更復雜,
有沒有安全方法進行多執行緒編程? 我們先學習一下‘執行緒安全’了,
分享之前,學委把上次送書的視頻公開發布到B站:
不定期抽獎第二期 - 定時抽獎實測
什么是執行緒安全?
執行緒安全,名字就非常直接,在多執行緒情況下是安全的,多執行緒操作上的安全,
比如一個計算加法的函式,不管是一千個還是一萬個執行緒,我們希望它執行的結果總是正確的,1+1 必須永遠等于2, 而不是執行緒少的時候1+1 變成3或者4了,
通常我們都用執行緒安全來修飾一個類,修飾一個函式,
我們會說我設計的這個類是執行緒安全的
這意味著,在多執行緒環境下,同時呼叫這個類的函式不會出現函式設定預期之外的例外(上述的1+1=3的情況)
在Python中有哪些類是執行緒安全的?
dict 和 list,tuple這些都是執行緒安全,
它們是被全域解釋器保障了,這個鎖:GIL(全域解釋器鎖)確保了任何時候只能有一個執行緒執行相應操作的位元組碼,
https://docs.python.org/3/glossary.html#term-global-interpreter-lock

但是這番話也是說的不清不楚的,
現在我們拿轉賬來決議吧:
xuewei_account = dict()
xuewei_account['amount'] = 100
# amount為負數即是轉出金額
def transfer(money):
xuewei_account['amount'] += money
如上,代碼為一個函式對xuewei_account(賬戶)進行轉入金額操作,
這里用了dict型別,GIL會保證只有一個執行緒操作賬戶,
下面是多個執行緒進行操作的代碼:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/11/24 12:02 上午
# @Author : LeiXueWei
# @CSDN/Juejin/Wechat: 雷學委
# @XueWeiTag: CodingDemo
# @File : testthread_safe.py
# @Project : hello
import random
import threading
import datetime
import time
xuewei_account = dict()
xuewei_account['amount'] = 100
# amount為負數即是轉出金額
def transfer(money):
xuewei_account['amount'] += money
# 創建4個任務給重復學委賬戶轉賬
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來等待所有執行緒執行完畢
# join函式必須執行緒start后才能呼叫,否則出錯,
for t in threads:
t.join()
print("-" * 16)
print("活躍執行緒數:", threading.active_count())
print("活躍執行緒:", threading.current_thread().name)
print("學委賬戶余額:", xuewei_account)
這段代碼運行的輸出結果正常,因為是反復+1/-1,最后肯定是恢復原賬戶余額,
雖然多個執行緒,但是每個執行緒只對xuewei_account進行一次讀寫,這時候dict是安全的,

但是我們把賦值修改dict的操作變多之后(特別是一個執行緒內反復多次獲取值然后修改),像下面的代碼:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/11/24 12:02 上午
# @Author : LeiXueWei
# @CSDN/Juejin/Wechat: 雷學委
# @XueWeiTag: CodingDemo
# @File : testthread_safe.py
# @Project : hello
import random
import threading
import datetime
import time
xuewei_account = dict()
xuewei_account['amount'] = 100
# amount為負數即是轉出金額
def transfer(money):
for i in range(100000):
xuewei_account['amount'] = xuewei_account['amount'] + money
# 創建400個任務重復給學委賬戶轉賬
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("活躍執行緒數:", threading.active_count())
print("活躍執行緒:", threading.current_thread().name)
print("學委賬戶余額:", xuewei_account)
這是某一次運行結果(不保證每次acount的數值一樣):

我們看到dict還是扛不住多個執行緒反復的寫操作,
這里區別是:每個執行緒只對xuewei_account進行大量讀寫,雖然dict是安全的,但是多個執行緒中間穿插修改了account,程式方法堆疊出現操作到舊值(看下面的圖),
主要是下面這段代碼:
xuewei_account[‘amount’] += money # 即是 xuewei_account[‘amount’] = xuewei_account[‘amount’]+ money
再一步抽象簡化可以寫成:
a = a + b
每個執行緒都執行 +b 操作,最后a的值應該是a+2b,
上面的操作意味這下面的情況發生了:

在某個執行緒中可能出現某一個執行緒T1獲取了a值 ,準備加上b,
另外一個執行緒T2已經完成了a+b操作,把a的值變成了a+b了,
但是接下來T1 拿了a的值再執行a+b操作,把a的值變成a+b,
這樣就少加了一個b,本來最后結果是a+2b 的變成了 a+b(因為T1拿了a的舊值,中間T2執行完,T1才繼續執行)
當然實際多執行緒之間互動比上圖還要隨機,
如何做到真正執行緒安全?
dict讀取資料是執行緒安全,但是被反復讀寫就容易出現資料混亂,
如果我們要設計一個執行緒安全的函式,那么它必須不涉及任何共享變數或者是完全沒有狀態依賴的函式
def thread_safe_method():
pass
無狀態函式
比如下面的加法函式,不管多少個執行緒呼叫,回傳值永遠是預期的a+b,
def add(a, b):
return a + b
另一種 化繁為簡
或許我們可以把多執行緒轉換為單執行緒,這個需要一個執行緒安全的媒介,
也就是下一篇講到的:執行緒安全佇列,
書籍贈送 :《Axure RP 原型設計實踐(Web+APP)》

簡介
《Axure RP 原型設計實踐(Web+APP)》是一本通過大量案例介紹Axure RP原型設計的教程,
全書分為三篇,分別為Axure RP基礎、Axure RP高級功能和Axure RP原型設計實踐,包括產品原型設計、Axure RP概述、基礎元件、高級元件、元件互動、母版、Axure Share共享原型、團隊專案、輸出檔案、Web原型設計實踐、App原型設計實踐、選單原型設計實踐、整站原型設計——溫馨小居共13章內容,附錄部分對原型設計中的眾多常見問題專門進行了解答,
《Axure RP 原型設計實踐(Web+APP)》主要面向產品經理、需求分析師、架構師、用戶體驗設計師、網站策劃師、互動設計師、產品助理等,以及高校計算機及相關專業師生,
目錄和更多書籍資訊
https://item.jd.com/13375632.html
https://e.dangdang.com/products/1901162790.html
好用的原型設計工具可以快速的制作產品(MVP),不限于網站/App,Axure RP 這個工具還是應用非常廣泛的,
各位開發讀者們想要把應用做的更加專業,有條理,可以多看看這類資料,了解產品設計,可以程式更加產品化,專業化,更好互動,而不是天馬行空堆一個四不像,
學委這里想分享的是,有些業務可以開發為N個介面協作,配套界面N個操作做完,但是通過高效產品設計,可以把這個互動程序做的很人性化,很友好,就像一切剛出蘋果手機一樣,簡潔又傻瓜式的操作,
特別是有些獨立開發者,一條龍全包了專案,包括產品設計,開發編碼發布等,值得入手,
對了,喜歡Python的朋友,請關注學委的 Python基礎專欄 or Python入門到精通大專欄
持續學習持續開發,我是雷學委!
編程很有趣,關鍵是把技術搞透徹講明白,
歡迎關注微信,點贊支持收藏!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/385501.html
標籤:其他
