Python的線程使用隊列Queue來改造轉賬場景
前篇我們瞭隊列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解決並發轉賬問題。
其實代碼還可以再度優化的,為瞭控制篇幅,代碼也不少,希望讀者朋友們能夠先看熟學會,掌握隊列的使用。
到此這篇關於Python的線程使用隊列來改造轉賬場景的文章就介紹到這瞭,更多相關Python使用隊列來改造轉賬場景內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!