Python 并發(fā)編程之線程鎖
線程 --> 鎖
GIL 是python 解釋器全局鎖
多個(gè)線程搶占資源的情況:
from?threading?import?Thread import?os,time def?work(): ????global?n ????temp=n ????time.sleep(0.1) ????n=temp-1 if?__name__?==?'__main__': ????n=100 ????l=[] ????for?i?in?range(100): ????????p=Thread(target=work) ????????l.append(p) ????????p.start() ????for?p?in?l: ????????p.join() ????print(n)?#結(jié)果可能為99
import?threading R=threading.Lock() R.acquire() ''' 對(duì)公共數(shù)據(jù)的操作 '''
同步鎖的引用:
from?threading?import?Thread,Lock import?os,time def?work(): ????global?n ????lock.acquire() ????temp=n ????time.sleep(0.1) ????n=temp-1 ????lock.release() if?__name__?==?'__main__': ????lock=Lock() ????n=100 ????l=[] ????for?i?in?range(100): ????????p=Thread(target=work) ????????l.append(p) ????????p.start() ????for?p?in?l: ????????p.join() ????print(n)?#結(jié)果肯定為0,由原來(lái)的并發(fā)執(zhí)行變成串行,犧牲了執(zhí)行效率保證了數(shù)據(jù)安全
互斥鎖與join的區(qū)別:
#不加鎖:并發(fā)執(zhí)行,速度快,數(shù)據(jù)不安全 from?threading?import?current_thread,Thread,Lock import?os,time def?task(): ????global?n ????print('%s?is?running'?%current_thread().getName()) ????temp=n ????time.sleep(0.5) ????n=temp-1 if?__name__?==?'__main__': ????n=100 ????lock=Lock() ????threads=[] ????start_time=time.time() ????for?i?in?range(100): ????????t=Thread(target=task) ????????threads.append(t) ????????t.start() ????for?t?in?threads: ????????t.join() ????stop_time=time.time() ????print('主:%s?n:%s'?%(stop_time-start_time,n)) ''' Thread-1?is?running Thread-2?is?running ...... Thread-100?is?running 主:0.5216062068939209?n:99 ''' #不加鎖:未加鎖部分并發(fā)執(zhí)行,加鎖部分串行執(zhí)行,速度慢,數(shù)據(jù)安全 from?threading?import?current_thread,Thread,Lock import?os,time def?task(): ????#未加鎖的代碼并發(fā)運(yùn)行 ????time.sleep(3) ????print('%s?start?to?run'?%current_thread().getName()) ????global?n ????#加鎖的代碼串行運(yùn)行 ????lock.acquire() ????temp=n ????time.sleep(0.5) ????n=temp-1 ????lock.release() if?__name__?==?'__main__': ????n=100 ????lock=Lock() ????threads=[] ????start_time=time.time() ????for?i?in?range(100): ????????t=Thread(target=task) ????????threads.append(t) ????????t.start() ????for?t?in?threads: ????????t.join() ????stop_time=time.time() ????print('主:%s?n:%s'?%(stop_time-start_time,n)) ''' Thread-1?is?running Thread-2?is?running ...... Thread-100?is?running 主:53.294203758239746?n:0 ''' #有的同學(xué)可能有疑問(wèn):既然加鎖會(huì)讓運(yùn)行變成串行,那么我在start之后立即使用join,就不用加鎖了啊,也是串行的效果啊 #沒(méi)錯(cuò):在start之后立刻使用jion,肯定會(huì)將100個(gè)任務(wù)的執(zhí)行變成串行,毫無(wú)疑問(wèn),最終n的結(jié)果也肯定是0,是安全的,但問(wèn)題是 #start后立即join:任務(wù)內(nèi)的所有代碼都是串行執(zhí)行的,而加鎖,只是加鎖的部分即修改共享數(shù)據(jù)的部分是串行的 #單從保證數(shù)據(jù)安全方面,二者都可以實(shí)現(xiàn),但很明顯是加鎖的效率更高. from?threading?import?current_thread,Thread,Lock import?os,time def?task(): ????time.sleep(3) ????print('%s?start?to?run'?%current_thread().getName()) ????global?n ????temp=n ????time.sleep(0.5) ????n=temp-1 if?__name__?==?'__main__': ????n=100 ????lock=Lock() ????start_time=time.time() ????for?i?in?range(100): ????????t=Thread(target=task) ????????t.start() ????????t.join() ????stop_time=time.time() ????print('主:%s?n:%s'?%(stop_time-start_time,n)) ''' Thread-1?start?to?run Thread-2?start?to?run ...... Thread-100?start?to?run 主:350.6937336921692?n:0?#耗時(shí)是多么的恐怖 '''
進(jìn)程也有死鎖與遞歸鎖,在進(jìn)程那里忘記說(shuō)了,放到這里一切說(shuō)了額
所謂死鎖: 是指兩個(gè)或兩個(gè)以上的進(jìn)程或線程在執(zhí)行過(guò)程中,因爭(zhēng)奪資源而造成的一種互相等待的現(xiàn)象,若無(wú)外力作用,它們都將無(wú)法推進(jìn)下去。此時(shí)稱系統(tǒng)處于死鎖狀態(tài)或系統(tǒng)產(chǎn)生了死鎖,這些永遠(yuǎn)在互相等待的進(jìn)程稱為死鎖進(jìn)程,如下就是死鎖
死鎖:
from?threading?import?Lock?as?Lock import?time mutexA=Lock() mutexA.acquire() mutexA.acquire() print(123) mutexA.release() mutexA.release()
解決方法,遞歸鎖,在Python中為了支持在同一線程中多次請(qǐng)求同一資源,python提供了可重入鎖RLock。
這個(gè)RLock內(nèi)部維護(hù)著一個(gè)Lock和一個(gè)counter變量,counter記錄了acquire的次數(shù),從而使得資源可以被多次require。直到一個(gè)線程所有的acquire都被release,其他的線程才能獲得資源。上面的例子如果使用RLock代替Lock,則不會(huì)發(fā)生死鎖:
遞歸鎖:
from?threading?import?RLock?as?Lock import?time mutexA=Lock() mutexA.acquire() mutexA.acquire() print(123) mutexA.release() mutexA.release()
典型問(wèn)題:科學(xué)家吃面
死鎖問(wèn)題:
import?time from?threading?import?Thread,Lock noodle_lock?=?Lock() fork_lock?=?Lock() def?eat1(name): ????noodle_lock.acquire() ????print('%s?搶到了面條'%name) ????fork_lock.acquire() ????print('%s?搶到了叉子'%name) ????print('%s?吃面'%name) ????fork_lock.release() ????noodle_lock.release() def?eat2(name): ????fork_lock.acquire() ????print('%s?搶到了叉子'?%?name) ????time.sleep(1) ????noodle_lock.acquire() ????print('%s?搶到了面條'?%?name) ????print('%s?吃面'?%?name) ????noodle_lock.release() ????fork_lock.release() for?name?in?['哪吒','egon','yuan']: ????t1?=?Thread(target=eat1,args=(name,)) ????t2?=?Thread(target=eat2,args=(name,)) ????t1.start() ????t2.start()
遞歸鎖解決死鎖問(wèn)題:
import?time from?threading?import?Thread,RLock fork_lock?=?noodle_lock?=?RLock() def?eat1(name): ????noodle_lock.acquire() ????print('%s?搶到了面條'%name) ????fork_lock.acquire() ????print('%s?搶到了叉子'%name) ????print('%s?吃面'%name) ????fork_lock.release() ????noodle_lock.release() def?eat2(name): ????fork_lock.acquire() ????print('%s?搶到了叉子'?%?name) ????time.sleep(1) ????noodle_lock.acquire() ????print('%s?搶到了面條'?%?name) ????print('%s?吃面'?%?name) ????noodle_lock.release() ????fork_lock.release() for?name?in?['哪吒','egon','yuan']: ????t1?=?Thread(target=eat1,args=(name,)) ????t2?=?Thread(target=eat2,args=(name,)) ????t1.start() ????t2.start()
軟件開(kāi)發(fā) 人工智能 云計(jì)算 機(jī)器學(xué)習(xí)
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。