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!

推薦閱讀: