網站首頁 編程語言 正文
這次讓我們來看看一個真實場景吧:銀行轉賬
一、舉例銀行轉賬
假設現在有一個xuewei的賬號里面有 100W。
然后有多個任務在轉賬,轉入轉出都是跟這個xuewei賬號相關的。
而且這些任務發生是隨機的。
我們先把上面的場景寫成代碼:
xuewei_account = 100 # amount為負數即是轉出金額 def transfer(money): ? ? global xuewei_account ? ? xuewei_account += money
下面是多個線程,多線程模擬轉賬事件,我們假設有4個事件在同時發生。
import random import threading import datetime import time xuewei_account = 100 # amount為負數即是轉出金額 def transfer(money): ? ? global xuewei_account ? ? xuewei_account += money # 創建4個任務給學委賬戶轉賬 for i in range(10000): ? ? threading.Thread(target=lambda: transfer(-1)).start() ? ? threading.Thread(target=lambda: transfer(1)).start() ? ? threading.Thread(target=lambda: transfer(-1)).start() ? ? threading.Thread(target=lambda: transfer(1)).start() # 等待活躍線程只剩下主線程MainThread time.sleep(10) print("-" * 16) print("活躍線程數:", threading.active_count()) print("活躍線程:", threading.current_thread().name) print("學委賬戶余額:", xuewei_account)
這里啟動了4個線程循環了10000次,也就是4萬個線程,分別于學委的賬戶進行轉賬。
下面是運行結果:
運行幾次學委的賬戶還是正確的,余額還是100W。
上面的代碼線程幾萬個,但每次運行的操作都很簡單,完成一次加法。
線程一個接一個start,非常快速就切換下一個線程, 我們看到程序沒有出現問題。
下面進行改造,這次不要就4萬線程了,我們讓轉賬這個任務耗時更多,每啟動一個線程進行模擬10萬次轉賬。
import random import threading import datetime import time xuewei_account = 100 # amount為負數即是轉出金額 def transfer(money): ? ? global xuewei_account ? ? for x in range(100000): ? ? ? ? xuewei_account += money
?創建4個任務給重復學委賬戶轉賬:
for i in range(10): ? ? threading.Thread(target=lambda: transfer(-1)).start() ? ? threading.Thread(target=lambda: transfer(1)).start() ? ? threading.Thread(target=lambda: transfer(-1)).start() ? ? threading.Thread(target=lambda: transfer(1)).start() time.sleep(10) print("-" * 16) print("活躍線程數:", threading.active_count()) print("活躍線程:", threading.current_thread().name) print("學委賬戶余額:", xuewei_account)
這里運行的結果就比較出乎意料了:
多線程編程復雜的地方就在這里了, 有時候明明平平無奇的代碼,改造成多線程,就很容易出bug!
當然上面的代碼并不是平平無奇,相比第一段代碼,上面的轉賬函數做的事件更多,更耗時。
二、問題解決
我們加上鎖。
代碼如下:
import random import threading import datetime import time xuewei_account = 100 lock = threading.Lock() # amount為負數即是轉出金額 def transfer(money): ? ? lock.acquire() ? ? global xuewei_account ? ? for x in range(100000): ? ? ? ? xuewei_account += money ? ? lock.release() # 創建4個任務給重復學委賬戶轉賬 for i in range(10): ? ? threading.Thread(target=lambda: transfer(-1)).start() ? ? threading.Thread(target=lambda: transfer(1)).start() ? ? threading.Thread(target=lambda: transfer(-1)).start() ? ? threading.Thread(target=lambda: transfer(1)).start() time.sleep(10) print("-" * 16) print("活躍線程數:", threading.active_count()) print("活躍線程:", threading.current_thread().name) print("學委賬戶余額:", xuewei_account)
運行結果如下:
上面的代碼不管怎么運行,運行多少次最后學委的賬戶都是100.(PS:學委不會聯系讀者轉賬的,這個特別注意)。
不管多少個線程,每次轉賬函數內部轉賬的代碼(從global到 += money這一段代碼)只會被一個線程調用。
三、總結
展示了同步機制解決一些編程問題的思路。讀者可以多多借鑒,思考鎖的應用。
為什么在對amount重度操作(本文第二段代碼)的時候,計算就出錯了!
這里amount
相當于多線程都在操作的變量,也就是共享變量,多線程編程要特別注意這類變量,避免出現對共享變量的操作,有些程序在并發規模很小的時候一點問題也沒有。
并發編程是高度利用CPU計算能力的編程方式,并發程序也就是在并行執行同類任務的程序。這個可以跟單線程應用比較。
原文鏈接:https://levin.blog.csdn.net/article/details/121943332
相關推薦
- 2023-01-12 關于scipy.optimize函數使用及說明_python
- 2022-02-17 uni-app的 tabBar添加陰影
- 2022-09-11 windows?CMD生成文件夾樹狀圖tree命令的使用_DOS/BAT
- 2023-05-17 mongodb?root用戶創建數據庫提示not?master的解決_MongoDB
- 2022-05-15 C++的數據共享與保護你了解嗎_C 語言
- 2022-07-30 jQuery?UI菜單部件Menu?Widget_jquery
- 2022-01-12 nvm-windows使用與避坑指南,npm沒反應也不報錯怎么辦
- 2022-10-24 C++??STL?_?Vector使用及模擬實現_C 語言
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支