python線程同步鎖_[python] 線程間同步之Lock RLock

為什么需要同步

同樣舉之前的例子,兩個線程分別對同一個全局變量進行加減,得不到預期結果,代碼如下:

total = 0

def add():

global total

for i in range(1000000):

total += 1

def desc():

global total

for i in range(1000000):

total -= 1

import threading

thread1 = threading.Thread(target=add)

thread2 = threading.Thread(target=desc)

thread1.start()

thread2.start()

thread1.join()

thread2.join()

print(total)

原因就是因為 += 和 -=并不是原子操作。可以使用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

可以看到 add()函數雖然其中只有一行代碼,但是字節碼主要分為四個步驟:

load 變量total

load 常量 1

執行加法操作

對total進行賦值

同理,desc()函數的步驟相同,只是第三步改為執行減法。

假設一種極端情況,開始total = 0,首先線程1 load 變量total,得到值為0,切換到線程2,同樣的到total為0,再次切換線程1 load常量1,執行加法,給total賦值得到1;然后線程2也 laod常量1,執行減法,給total賦值為-1。最終total為-1,而不是預期的0。

期望中,必須在+=操作結束后,才能執行-=,所以線程同步的需求就出來了。

互斥鎖Lock

threading模塊中提供了threading.Lock類(互斥鎖),基本用法如下:

import threading

lock = threading.Lock()

lock.acquire() # 獲取鎖

# dosomething…… # 臨界區的代碼只能被同時只能被一個線程運行

lock.release() # 釋放鎖

將上面的代碼修改,即可得到正確結果:

import threading

total = 0

lock = threading.Lock()

def add(lock):

global total

for i in range(1000000):

lock.acquire()

total += 1

lock.release()

def desc(lock):

global total

for i in range(1000000):

lock.acquire()

total -= 1

lock.release()

thread1 = threading.Thread(target=add, args=(lock,))

thread2 = threading.Thread(target=desc, args=(lock,))

thread1.start()

thread2.start()

thread1.join()

thread2.join()

print(total)

# 執行結果 0

可以看到,添加互斥鎖之后,程序執行結果是正確的,但是用了互斥鎖之后,同樣有一些缺陷:

添加鎖之后,會影響程序性能。

可能引起"死鎖"。

死鎖主要在兩種情況下發生:

迭代死鎖

一個線程“迭代”請求同一個資源 ,會造成死鎖。

lock = threading.Lock()

lock.acquire()

lock.acquire()

total += 1

lock.release()

lock.release()

上例種,第一次請求資源后還未 release ,再次acquire,最終無法釋放,造成死鎖 。(可通過可重入鎖解決這個問題)。

互相調用死鎖

兩個線程中都會調用相同的資源,互相等待對方結束的情況 。假設A線程需要資源a,b,B線程也需要資源a,b,而線程A先獲取資源a后,再獲取資源b, B線程先獲取資源b,再獲取資源a。在A線程獲取資源a,B線程獲取資源b后,A線程在等待B線程釋放資源b,而B線程在等待A線程釋放資源a,從而死鎖就發生了。

import threading

import time

lock_a = threading.Lock()

lock_b = threading.Lock()

def func1():

global lock_a

global lock_b

lock_a.acquire()

time.sleep(1)

lock_b.acquire()

time.sleep(1)

lock_b.release()

lock_a.release()

def func2():

global lock_a

global lock_b

lock_b.acquire()

time.sleep(1)

lock_a.acquire()

time.sleep(1)

lock_a.release()

lock_b.release()

thread1 = threading.Thread(target=func1)

thread2 = threading.Thread(target=func2)

thread1.start()

thread2.start()

thread1.join()

thread2.join()

print("program finished")

# 程序會陷入死循環

這個例子比較重要,開始理解錯了,如果B線程獲取了資源b,然后釋放之后再獲取資源a,這樣是不會發生死鎖的。只有在B線程獲取了資源b,還沒有釋放的時候,獲取了資源a,才會發生死鎖。

可重入鎖RLock

為解決同一線程種不能多次請求同一資源的問題,python提供了“可重入鎖”:threading.RLock,RLock內部維護著一個Lock和一個counter變量,counter記錄了acquire的次數,從而使得資源可以被多次require。直到一個線程所有的acquire都被release,其他的線程才能獲得資源 。用法和threading.Lock類相同。

將上面迭代死鎖的代碼改寫一下,就不會發生死鎖,但注意,調用acquire和release的次數必須相等。

lock = threading.RLock()

lock.acquire()

lock.acquire()

total += 1

lock.release()

lock.release()

一般不會寫這么無聊的代碼,但是有一種情況是可能發生的,在加鎖區域調用了某個函數,而這個函數內部又申請了同樣的資源。

lock = threading.RLock()

def dosomething(lock):

lock.acquire()

# do something

lock.release()

lock.acquire()

dosomething(lock)

lock.release()

總結

線程間訪問同一變量需要同步。

線程間加鎖會導致性能損失。

加鎖可能產生死鎖,迭代死鎖和互相調用死鎖。

可重入鎖可以避免迭代死鎖。

參考

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/542520.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/542520.shtml
英文地址,請注明出處:http://en.pswp.cn/news/542520.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

servlet的由來

2019獨角獸企業重金招聘Python工程師標準>>> 動靜態網頁技術 首先說下訪問網頁的大概過程: 你在瀏覽器中輸入網址,按下enter鍵,此時瀏覽器代你做了很多事,簡要說為:將你輸入的這個網址作為目的地參數&#…

php header 文件大小,php獲取遠程文件大小及信息的函數(head_php

php獲取遠程文件大小及信息的函數(header頭信息獲取)阿里西西Alixixi.com開發團隊在做一個客戶系統時,需要做遠程下載的功能,并實時顯示進度條效果。所以,需要預先讀取遠程文件的大小信息,然后做為實時下載進度條的參數。功能函數…

Java ObjectInputStream readUnsignedShort()方法(帶示例)

ObjectInputStream類readUnsignedShort()方法 (ObjectInputStream Class readUnsignedShort() method) readUnsignedShort() method is available in java.io package. readUnsignedShort()方法在java.io包中可用。 readUnsignedShort() method is used to read 2 bytes (i.e. …

python中info的用法_Python pandas.DataFrame.info函數方法的使用

DataFrame.info(self, verboseNone, bufNone, max_colsNone, memory_usageNone, null_countsNone) [source] 打印DataFrame的簡要摘要。 此方法顯示有關DataFrame的信息,包括索引dtype和列dtype,非空值和內存使用情況。 參數:verbose &#x…

第四次作業 孫保平034 李路平029

用C編寫一個學生成績管理系統 1、可以實現以下功能&#xff1a; cout<<"〓〓〓〓〓〓〓〓〓★ ☆ 1.增加學生成績 ☆ ★〓〓〓〓〓〓〓〓〓"<<endl; 2、用鏈表存儲信息 * 程序頭部的注釋結束 3、約定的規范&#xff1a; 1界面設計簡介&#xff0c;人性化…

php serialize error at offset,PHP Notice: unserialize(): Error at offset XX of XX bytes

之前同事在本地開發的時候&#xff0c;出現一個錯誤&#xff0c;如下圖所示&#xff1a;字面意思就是反序列化錯誤&#xff0c;由此bug引申出來序列化和反序列化得應用&#xff0c;以及php array當key為string類型的數字值時&#xff0c;會發生什么情形。先來看序列化$str [1 …

Java ClassLoader setClassAssertionStatus()方法與示例

ClassLoader類setClassAssertionStatus()方法 (ClassLoader Class setClassAssertionStatus() method) setClassAssertionStatus() method is available in java.lang package. setClassAssertionStatus()方法在java.lang包中可用。 setClassAssertionStatus() method is used …

python怎么變各種顏色_python – 如何淡化顏色

有很多方法可以做到這一點.您如何選擇這取決于您是否重視速度和簡單性或感知均勻性.如果你需要它是真正統一的,你需要用顏色配置文件定義RGB顏色,你需要配置文件的原色,這樣你就可以轉換為XYZ,然后轉換到LAB,你可以操作L通道. 大多數情況下,您不需要這樣做,而是可以使用像Photo…

informatica中元數據管理

摘自&#xff1a; http://blog.itpub.net/28690368/viewspace-766528/ informaica是一個很強大的ETL工具&#xff0c;WORKFLOW MANAGER負責對ETL調度流程進行設計與管理和執行&#xff0c;informatica在資料庫中提供以下表來存儲調動流程的相關信息&#xff0c;以便WORKFLOW …

yii+php+當前目錄,Yii應用的目錄結構和入口腳本

以下是一個通過高級模版安裝后典型的Yii應用的目錄結構&#xff1a;~~~.├── backend├── common├── console├── environments├── frontend├── nbproject├── tests├── vendor├── composer.json├── composer.lock├── init├── init.bat├── …

8086 尋址方式_8086微處理器的不同尋址模式

8086 尋址方式Introduction: 介紹&#xff1a; Addressing mode tells us what is the type of the operand and the way they are accessed from the memory for execution of an instruction and how to fetch particular instruction from the memory. There are mainly 8 …

決策樹的value是什么意思_從零開始的機器學習實用指南(六):決策樹

類似SVM&#xff0c;決策樹也是非常多功能的機器學習算法&#xff0c;可以分類&#xff0c;回歸&#xff0c;甚至可以完成多輸出的任務&#xff0c;能夠擬合復雜的數據集&#xff08;比如第二章的房價預測例子&#xff0c;雖然是過擬合了。&#xff09;決策樹也是很多集成學習的…

Hive中生成隨機唯一標識ID的方法

2019獨角獸企業重金招聘Python工程師標準>>> HIVE中處理的數據往往比較多&#xff0c;在處理數據的時候希望給處理得到的數據一個ID標識&#xff0c;這時候可以用到UUID。 UUID的算法的核心思想是結合機器的網卡、當地時間、一個隨即數來生成UUID。從理論上講&#…

php從網頁獲得數據,php根據URL獲得網頁內容

php 中根據url來獲得網頁內容非常的方便&#xff0c;可以通過系統內置函數file_get_contents(),傳入url,即可返回網頁的內容&#xff0c;比如獲得百度首頁的內容代碼為&#xff1a;$html file_get_contents(http://www.baidu.com/);echo $html;就可以顯示出百度首頁的內容&…

2020知道python語言應用答案_2020知到Python語言應用答案章節期末答案

組合管理理論最早由哈維馬科維茲于1962年系統的提出&#xff0c;他開創了對投資進行整體管理的先 公司型基金和契約型基金的區別&#xff0c;下列不包括&#xff08;&#xff09;。 A&#xff0e;資金的性質B&#xff0e;基金的營運依據C&#xff0e;基 我國&#xff08;&#…

如何在Bootstrap中使用Jumbotron和頁面標頭類?

Introduction 介紹 In the previous article, we have learned how Responsive column, Nesting Columns and offset Columns work and how to use them? I hope now, you all are comfortable with the grid system; what is it, how to use it and how we can use it for c…

python中的數字類型格式與運算,python數字數據類型

python數字數據類型1. 數字在我們很小的時候&#xff0c;父母便開始教我們數數&#xff0c;從1數到10&#xff0c;聰明的孩子可以數的更多。python支持3中數值類型整型(int)&#xff0c;通常稱之為整型或整數&#xff0c;這個概念與我們小學時學過的整數是相同的&#xff0c;py…

getprocaddress得到為0_基于ZU+系列MPSoC芯片的USB3.0/2.0接口硬件設計

本文主要介紹Zynq UltraScale MPSoC系列芯片的USB3.0/2.0接口硬件設計。ZU系列MPSoC要實現USB3.0/2.0的全部功能&#xff0c;需要同時使用MIO和GTR。因為GTR接口中的USB接口只支持USB3.0&#xff0c;對USB2.0的支持需要通過MIO接口外接USB PHY實現。ZU系列MPSoC包括兩個USB接口…

如何設置單詞第一個字母大寫_大寫一行中每個單詞的第一個和最后一個字母

如何設置單詞第一個字母大寫Problem statement: 問題陳述&#xff1a; Given an input line, capitalize first and last letter of each word in the given line. Its provided that the input line is in lowercase. 給定輸入行&#xff0c; 將給定行中每個單詞的第一個和最…

php如何編造簡歷,在簡歷里編造內容需要注意哪些問題?

在個人簡歷里編造內容可得有一定依據才行&#xff0c;總得為自己后期做個準備工作是不是&#xff1f;你編造的東西不只是給企業看一看而已&#xff0c;企業還會對這些內容作出進一步的判斷&#xff0c;并且可能就其對你進行提問&#xff0c;如果你答不出來而曝光自己是在欺騙企…