日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

Python線程之同步機制實際應用場景舉例說明_python

作者:雷學委 ? 更新時間: 2022-04-27 編程語言

這次讓我們來看看一個真實場景吧:銀行轉賬

一、舉例銀行轉賬

假設現在有一個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

欄目分類
最近更新