哈嘍大家好,我是了不起,今天來給大家介紹關于Python中的線程,threading庫。
在Python中,threading庫提供了一種簡單且方便的方式來實現多線程編程。通過使用線程,可以在程序中并行執行多個任務,提高程序的性能和響應性。
線程是程序執行的最小單元,是操作系統能夠進行運算調度的基本單位。與進程不同,線程在同一進程下共享相同的內存空間,因此線程之間的通信更加方便。在Python中,threading庫提供了對線程的支持。
threading庫是Python中的標準庫,無需下載,我們只需在文件中導入threading庫就可以用了。
創建線程的時候主要有兩種方式,第一種是通過繼承threading.Thread類,第二種則是通過傳遞可調用對象給threading.Thread的構造函數,接下來先講解第一種方式。
import threadingclass MyThread(threading.Thread): def __init__(self, name): super(MyThread, self).__init__() self.name = name def run(self): print(f"Thread {self.name} is running.")# 創建線程的實例thread1 = MyThread(name="Thread 1")thread2 = MyThread(name="Thread 2")# 啟動線程thread1.start()thread2.start()# 等待線程執行完畢thread1.join()thread2.join()print("Main thread is done.")
第一種方式是最常見的方式,創建線程的時候需要先創建一個類,然后繼承threading.Thread,然后再我們創建的類中自定義一個方法,這里我構造的是run方法,在這個方法中我們可以去實現線程需要執行的主要邏輯。
然后通過thread1和thread2創建對應的構造實例,使用線程中的start()方法去啟動線程,最后在使用join()等到線程執行完畢,這樣我們創建了一個基本的多線程,執行后結果如下:
然后我們再來了解第二種創建線程的方式。
import threadingdef my_function(name): print(f"Thread {name} is running.")# 創建線程的實例,傳遞一個可調用對象和參數thread1 = threading.Thread(target=my_function, args=("Thread 1",))thread2 = threading.Thread(target=my_function, args=("Thread 2",))# 啟動線程thread1.start()thread2.start()# 等待線程執行完畢thread1.join()thread2.join()print("Main thread is done.")
這種方式我們是直接通過傳遞給一個可調用對象給threading.Thread的構造函數,我們所傳遞的這個可執行對象可以是函數、方法、或者是__call__等方法類的實例,
其中在threading.Thread實例中,通過使用target參數指定我們需要調用的對象,注意這里指定調用對象是不需要加括號,直接傳需要調用的可執行對象名就行,后面就和上面一樣,通過使用start()方法和join()方法,執行結果也是跟第一種方式一樣。
以上兩種方式都可以創建線程,選擇那種一般取決于個人在項目中的代碼風格和偏好,但是最終都是需要確保的是,無論使用哪種方式我們都需要保證在調用的方法中包含有線程的主要邏輯。
Python中的線程和其他語言中的線程邏輯也是一樣,如果創建了多個線程,那么這幾個線程就是共享內存,可能會導致數據競爭和不確定的結果,所以我們需要在線程中加鎖(lock)。
在python中,如果需要對線程加鎖我們就需要用到threading.lock()這個方法:
import threading# 共享資源counter = 0# 創建鎖對象my_lock = threading.Lock()def increment_counter(): global counter for _ in range(1000000): with my_lock: counter += 1# 創建兩個線程,分別增加計數器的值thread1 = threading.Thread(target=increment_counter)thread2 = threading.Thread(target=increment_counter)# 啟動線程thread1.start()thread2.start()# 等待兩個線程執行完畢thread1.join()thread2.join()print(f"Final counter value: {counter}")
在上述代碼中,我們通過創建了一個全局鎖對象,然后在調用的可執行對象中,使用with語句來獲取鎖和釋放鎖,以此來確保線程共享的資源是原子的。這樣可以避免多個線程對counter的參數結果進行數據競爭。
從這個簡單的代碼上我們可能看不出執行后實際有什么不同,接下來我舉一個例子來說明沒有加鎖和加了鎖后的執行結果。
import threadingclass BankAccount: def __init__(self, balance): self.balance = balance def withdraw(self, amount): current_balance = self.balance new_balance = current_balance - amount # 模擬取款操作的延遲 threading.Event().wait(0.1) self.balance = new_balance return new_balance# 創建一個共享的銀行賬戶account = BankAccount(balance=1000)def withdraw_from_account(account, amount): for _ in range(3): new_balance = account.withdraw(amount) print(f"Withdraw {amount}, New Balance: {new_balance}")# 創建兩個線程進行取款操作thread1 = threading.Thread(target=withdraw_from_account, args=(account, 100))thread2 = threading.Thread(target=withdraw_from_account, args=(account, 150))# 啟動兩個線程thread1.start()thread2.start()# 等待兩個線程執行完畢thread1.join()thread2.join()print(f"Final Balance: {account.balance}")
執行結果:
在上面這個不加鎖的實例中,我們用withdraw方法來模擬取款操作,然后通過兩個線程來對同時對賬戶進行取款操作,但是由于這個實例中沒有加鎖,就會出現下面的情況:
就這樣,本來是同一個賬戶,但是兩個線程都是各管各的,最后導致兩個線程都取了3次錢后,最后得出的結果是賬戶里面還剩了550元。
接下來我們再看看加鎖后的執行結果:
import threadingclass BankAccount: def __init__(self, balance): self.balance = balance self.lock = threading.Lock() def withdraw(self, amount): with self.lock: current_balance = self.balance new_balance = current_balance - amount # 模擬取款操作的延遲 threading.Event().wait(0.1) self.balance = new_balance return new_balance# 創建一個共享的銀行賬戶account = BankAccount(balance=1000)def withdraw_from_account(account, amount): for _ in range(3): new_balance = account.withdraw(amount) print(f"Withdraw {amount}, New Balance: {new_balance}")# 創建兩個線程進行取款操作thread1 = threading.Thread(target=withdraw_from_account, args=(account, 100))thread2 = threading.Thread(target=withdraw_from_account, args=(account, 150))# 啟動兩個線程thread1.start()thread2.start()# 等待兩個線程執行完畢thread1.join()thread2.join()print(f"Final Balance: {account.balance}")
同樣的實例,我們通過在實例中加鎖后再去執行,結果如下:
通過在實例中添加with self.lock后,我們保證了兩個線程訪問余額blance的原子性,不管是有多少個線程,每個線程訪問的余額始終是其他線程取錢后的最新結果,這樣就保證了代碼程序執行后的結果是正確的。
以上是今天分享的關于Python中一些基本的線程使用,有興趣的小伙伴想要深入學習threading這個模塊的話可以在留言區打出threading,人多的話我下期就繼續更新這個模塊。
本文鏈接:http://www.www897cc.com/showinfo-26-61905-0.htmlPython系列:多線程(threading)的學習和使用
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
上一篇: 高可靠的跨系統轉賬如何設計