網站首頁 編程語言 正文
前篇我們了隊列Queue和轉賬場景這次趁熱學委展示一下使用隊列解決轉賬場景的問題。
一、看看轉賬場景的問題
前面有兩篇文章展示了轉賬反復讀寫amount
,導致結果出錯。
xuewei_account = dict() xuewei_account['amount'] = 100 # amount為負數即是轉出金額 def transfer(money): ? ? for i in range(100000): ? ? ? ? xuewei_account['amount'] = xuewei_account['amount'] + money
我們前幾篇使用多個線程反復轉長:+1和-1。
按常理,結果應該仍舊是100.
這個是全部代碼:
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 # 創建20個任務重復給學委賬戶轉賬 threads = [] for i in range(10): ? ? 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)
等待所有轉賬線程運行結束,我們看到結果是錯誤的:
二、這種問題怎么使用隊列來解決呢?
前面說了,多線程反復讀寫共享數據,是問題的根源。
改代碼為同步互斥模式,保證任意一個時間一個線程更新共享數據,那么問題就解決了。(這前面也展示了,用的是Lock鎖的方案)
這個能怎么用隊列呢?
可以先思考10秒,根據學習到的加鎖和隊列的特性,想想這個怎么做。
好,答案現在揭曉:
Queue這個隊列有多個函數,一個是put函數,一個是get函數。
一個負責放入數據到隊尾,一個可以從對頭取出元素。
剛好適合轉賬業務,我們是不是可以把每次轉賬操作變成一個一個指令/事件。 比如下面的:
event(amount=1,acount=xuewei_account) .... event(amount=-1,acount=xuewei_account) .... event(amount=1,acount=xuewei_account) .... event(amount=-1,acount=xuewei_account)
20個線程,每個10萬次數據讀寫,共200萬個事件。
所以我們可以把這個事情轉換為:200萬個轉賬事件。
因為Queue是線程安全的,所以我們可以并發200萬次轉賬,另外交給一線程進行轉賬處理。
這樣就保證每次只有一個線程對xuewei_account
學委賬戶進行讀寫。
改造,使用隊列來解決問題
展示代碼:
import random import threading import datetime import time import queue q = queue.Queue() xuewei_account = dict() xuewei_account['amount'] = 100 # amount為負數即是轉出金額 def transfer(money): ? ? for i in range(100000): ? ? ? ? q.put(money) def handle_amount(): ? ? while not q.empty(): ? ? ? ? amount = q.get() ? ? ? ? xuewei_account['amount'] += amount def monitor_q(): ? ? counter = 0 ? ? time.sleep(3) ? ? while counter < 1000 and not q.empty(): ? ? ? ? print("q size:", q.qsize()) ? ? ? ? time.sleep(3) ? ? ? ? counter+=1 q_thread = threading.Thread(name="Q監控", target=monitor_q) q_thread.start() # 創建20個任務重復給學委賬戶轉賬 threads = [] for i in range(10): ? ? 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() vip_thread = threading.Thread(name="處理轉賬專線", target=handle_amount) vip_thread.start() for t in threads: ? ? t.join() vip_thread.join() print("-" * 16) print("活躍線程數:", threading.active_count()) print("活躍線程:", threading.current_thread().name) print("學委賬戶余額:", xuewei_account)
這里運行了多個線程執行轉賬(發送轉賬金額進隊列)。
然后運行一個vip通道(單獨線程)處理學委賬戶的轉賬業務。
同時也運行了一個監控隊列的線程,每隔一段時間打印隊列的任務情況。
下面是運行結果,運行幾次結果都是正確的。
運行幾次最終賬戶余額都是100, 改造成功。
三、總結
本篇學委分享了線程安全的隊列Queue解決并發轉賬問題。
其實代碼還可以再度優化的,為了控制篇幅,代碼也不少,希望讀者朋友們能夠先看熟學會,掌握隊列的使用。
原文鏈接:https://levin.blog.csdn.net/article/details/122052853
相關推薦
- 2022-09-19 用正則表達式匹配字符串中漢字及中文標點符號_正則表達式
- 2022-05-23 ELK與Grafana聯合打造可視化監控來分析nginx日志_nginx
- 2024-07-13 spring-cloud和spring-cloud-alibaba的關系
- 2022-03-31 C語言與C++項目實現相互調用_C 語言
- 2022-07-10 Linux安裝及管理程序
- 2022-12-13 Python使用Matplotlib繪制三維散點圖詳解流程_python
- 2023-10-14 SqlServer--get 和 post 請求 http接口
- 2022-12-02 python中os庫的函數使用_python
- 最近更新
-
- 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同步修改后的遠程分支