Python作為一門比較常見的編程語言,可以對其進行多線程的編程,包括利用Lock與RLock,實現多線程之間的相互同步,那么,實現這種原理的機制到底是什么樣子的呢?下面就給大家詳細解答下。
什么是鎖機制?
要回答這個問題,我們需要知道為什么需要使用鎖機制。前面我們談到一個進程內的多個線程的某些資源是共享的,這也是線程的一大優勢,但是也隨之帶來一個問題,即當兩個及兩個以上的線程同時訪問共享資源時,如果此時沒有預設對應的同步機制,就可能帶來同一時刻多個線程同時訪問同一個共享資源,即出現競態,多數情況下我們是不希望出現這樣的情況的,那么怎么避免呢?
Lock()管理線程
先看一段代碼:
import threading import time resource=0 count=1000000 resource_lock=threading.Lock() def increment(): global resource for i in range(count): resource+=1 def decerment(): global resource for i in range(count): resource-=1 increment_thread=threading.Thread(target=increment) decerment_thread=threading.Thread(target=decerment) increment_thread.start() decerment_thread.start() increment_thread.join() decerment_thread.join() print(resource)
運行截圖如下:
運行結果
當我們多次運行時,可以看到最終的結果都幾乎不等于我們期待的值即resource初始值0。
為什么呢?原因就是因為+=和-=并不是原子操作。
可以使用dis模塊查看字節碼:
import dis def add(total): total+=1 def desc(total): total-=1 total=0 print(dis.dis(add)) print(dis.dis(desc)) #運行結果: #3 0 LOAD_FAST 0(total) #3 LOAD_CONST 1(1) #6 INPLACE_ADD #7 STORE_FAST 0(total) #10 LOAD_CONST 0(None) #13 RETURN_VALUE #None #5 0 LOAD_FAST 0(total) #3 LOAD_CONST 1(1) #6 INPLACE_SUBTRACT #7 STORE_FAST 0(total) #10 LOAD_CONST 0(None) #13 RETURN_VALUE #None
那么如何保證初始值為0呢?我們可以利用Lock(),代碼如下:
import threading import time resource=0 count=1000000 resource_lock=threading.Lock() def increment(): global resource for i in range(count): resource_lock.acquire() resource+=1 resource_lock.release() def decerment(): global resource for i in range(count): resource_lock.acquire() resource-=1 resource_lock.release() increment_thread=threading.Thread(target=increment) decerment_thread=threading.Thread(target=decerment) increment_thread.start() decerment_thread.start() increment_thread.join() decerment_thread.join() print(resource)
運行截圖如下:
運行結果
從運行結果可以看到,不論我們運行多少次改代碼,其resource的值都為初始值0,這就是Lock()的功勞,即它可以將某一時刻的訪問限定在單個線程或者單個類型的線程上,在訪問鎖定的共享資源時,必須要現獲取對應的鎖才能訪問,即要等待其他線程釋放資源,即resource_lock.release()當然為了防止我們對某個資源鎖定后,忘記釋放鎖,導致死鎖,我們可以利用上下文管理器管理鎖實現同樣的效果:
import threading import time resource=0 count=1000000 resource_lock=threading.Lock() def increment(): global resource for i in range(count): with resource_lock: resource+=1 def decerment(): global resource for i in range(count): with resource_lock: resource-=1 increment_thread=threading.Thread(target=increment) decerment_thread=threading.Thread(target=decerment) increment_thread.start() decerment_thread.start()
RLock()與Lock()的區別
我們需要知道Lock()作為一個基本的鎖對象,一次只能一個鎖定,其余鎖請求,需等待鎖釋放后才能獲取,否則會發生死鎖:
import threading resource.lock=threading.lock() resource=0 resource.lock.acquire() resource.lock.acquire() resource+=1 resource.lock.release() resource.lock.release()
為解決同一線程中不能多次請求同一資源的問題,python提供了“可重入鎖”:threading.RLock,RLock內部維護著一個Lock和一個counter變量,counter記錄了acquire的次數,從而使得資源可以被多次acquire。
直到一個線程所有的acquire都被release,其他的線程才能獲得資源。用法和threading.Lock類相同,即比如遞歸鎖的使用:
import threading lock=threading.RLock() def dosomething(lock): lock.acquire() #do something lock.release() lock.acquire() dosomething(lock) lock.release()
綜上所述,這篇文章就給大家介紹到這里了,希望可以給大家帶來幫助。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/128429.html
摘要:如果某線程并未使用很多操作,它會在自己的時間片內一直占用處理器和。在中使用線程在和等大多數類系統上運行時,支持多線程編程。守護線程另一個避免使用模塊的原因是,它不支持守護線程。 這一篇是Python并發的第四篇,主要介紹進程和線程的定義,Python線程和全局解釋器鎖以及Python如何使用thread模塊處理并發 引言&動機 考慮一下這個場景,我們有10000條數據需要處理,處理每條...
摘要:某進程內的線程在其它進程不可見。線程的實體包括程序數據和。包括以下信息線程狀態。當線程不運行時,被保存的現場資源。用戶級線程執行系統調用指令時將導致其所屬進程被中斷,而內核支持線程執行系統調用指令時,只導致該線程被中斷。線程能夠利用的表空 操作系統線程理論 線程概念的引入背景 進程之前我們已經了解了操作系統中進程的概念,程序并不能單獨運行,只有將程序裝載到內存中,系統為它分配資源才能運...
摘要:本文最先發布在博客這篇文章將講解并發編程的基本操作。并發是指能夠多任務處理,并行則是是能夠同時多任務處理。雖然自帶了很好的類庫支持多線程進程編程,但眾所周知,因為的存在,很難做好真正的并行。 本文最先發布在博客:https://blog.ihypo.net/151628... 這篇文章將講解 Python 并發編程的基本操作。并發和并行是對孿生兄弟,概念經常混淆。并發是指能夠多任務處...
閱讀 911·2023-01-14 11:38
閱讀 878·2023-01-14 11:04
閱讀 740·2023-01-14 10:48
閱讀 1983·2023-01-14 10:34
閱讀 942·2023-01-14 10:24
閱讀 819·2023-01-14 10:18
閱讀 499·2023-01-14 10:09
閱讀 572·2023-01-14 10:02