python之进程和线程2
1? GIL全局解釋器鎖定義
定義:在一個(gè)線程擁有了解釋器的訪問權(quán)后,其他的所有線程都必須等待他釋放解釋器的訪問權(quán),即這些線程的下一條指令并不會(huì)互相影響。
缺點(diǎn):多處理器退化為單處理器
優(yōu)點(diǎn):避免大量的加鎖解鎖操作
?
?
無論你啟多少個(gè)線程,你有多少個(gè)cpu,python在執(zhí)行一個(gè)進(jìn)程的時(shí)候會(huì)淡定的在同一時(shí)刻只允許一個(gè)線程運(yùn)行。
Python是無法利用多核cpu實(shí)現(xiàn)多線程的
總結(jié):
對(duì)于計(jì)算密集型任務(wù),python的多線程并沒有用
對(duì)于IO密集型任務(wù),python的多線程是有意義的
python使用多核:開進(jìn)程,弊端:開銷大而且切換復(fù)雜
著重點(diǎn):協(xié)程+多進(jìn)程
方向:IO多路復(fù)用
終極思路:換C模塊實(shí)現(xiàn)多線程
2? 同步鎖
鎖通常被用來實(shí)現(xiàn)對(duì)共享資源的同步訪問,為每一個(gè)共享資源創(chuàng)建一個(gè)Lock對(duì)象,當(dāng)你需要訪問該資源時(shí),調(diào)用acquire方法來獲取鎖對(duì)象(如果其他線程已經(jīng)獲得了該鎖,則當(dāng)前線程需等待其被釋放),待資源訪問完,再調(diào)用release方法釋放鎖:
import time import threadingdef addNum():global num #在每個(gè)線程中都獲取這個(gè)全局變量#num-=1 #獲取的值為0 與temp=num num=temp-1是同一個(gè)意思 唯一在于time.sleep()lock.acquire()#添加鎖temp=numtime.sleep(0.001)#IO操作 如果時(shí)間在0.1時(shí),足夠讓一條線程完成后再走另一條線程 取值從100開始,99#如果時(shí)間為0.001時(shí),100條線程運(yùn)行時(shí)出現(xiàn)不確定性,有可能走到第五條的時(shí)候就完成IO操作,前五條基數(shù)為100,第六條基數(shù)就是99num =temp-1 # 對(duì)此公共變量進(jìn)行-1操作lock.release()#解鎖 num = 100 #設(shè)定一個(gè)共享變量thread_list = [] lock=threading.Lock()#獲取一把鎖 for i in range(100):t = threading.Thread(target=addNum)t.start()thread_list.append(t)for t in thread_list: #等待所有線程執(zhí)行完畢t.join()print('Result: ', num)
3 死鎖和遞歸鎖
死鎖就是指兩個(gè)或者兩個(gè)以上進(jìn)程或線程在執(zhí)行過程中,因爭(zhēng)奪資源而造成的一種互相等待的現(xiàn)象,若無外力作用,他們都將無法推進(jìn)下去。此時(shí)稱系統(tǒng)處于死鎖狀態(tài)或系統(tǒng)產(chǎn)生了死鎖這些永遠(yuǎn)在互相等待的進(jìn)程被稱為死鎖進(jìn)程。
import threading import timemutexA = threading.Lock() mutexB = threading.Lock()class MyThread(threading.Thread):def __init__(self):threading.Thread.__init__(self)def run(self):self.fun1()self.fun2()def fun1(self):mutexA.acquire() # 如果鎖被占用,則阻塞在這里,等待鎖的釋放print ("I am %s , get res: %s---%s" %(self.name, "ResA",time.time()))mutexB.acquire()print ("I am %s , get res: %s---%s" %(self.name, "ResB",time.time()))mutexB.release()mutexA.release()def fun2(self):mutexB.acquire()print ("I am %s , get res: %s---%s" %(self.name, "ResB",time.time()))time.sleep(0.2)mutexA.acquire()print ("I am %s , get res: %s---%s" %(self.name, "ResA",time.time()))mutexA.release()mutexB.release()if __name__ == "__main__":print("start---------------------------%s"%time.time())for i in range(0, 10):my_thread = MyThread()my_thread.start()
遞歸鎖
Python中為了支持在同一線程中多次請(qǐng)求統(tǒng)一資源,python提供了可重入鎖RLock。這個(gè)RLock內(nèi)部維護(hù)著一個(gè)Lock和一個(gè)counter變量,counter記錄了acquire的次數(shù),從而使得資源可以被多次require。直到一個(gè)線程所有的acpuire都被release,其他的線程才能獲得資源。
遞歸鎖用來解決死鎖
import threading import time Rlock=threading.RLock()#生成一個(gè)遞歸鎖 class MyThread(threading.Thread):def __init__(self):threading.Thread.__init__(self)def run(self):self.fun1()self.fun2()def fun1(self):Rlock.acquire() # 一條線程運(yùn)行,其他線程等待 count=1print ("I am %s , get res: %s---%s" %(self.name, "ResA",time.time()))Rlock.acquire()#同一條線程運(yùn)行,其他線程等待中。。。count=2,遞歸鎖print ("I am %s , get res: %s---%s" %(self.name, "ResB",time.time()))Rlock.release()#同一線程運(yùn)行完成后,count=1Rlock.release()#同一線程運(yùn)行完成后,count=0 ,其他線程才能獲得資源def fun2(self):Rlock.acquire()print ("I am %s , get res: %s---%s" %(self.name, "ResB",time.time()))Rlock.acquire()print ("I am %s , get res: %s---%s" %(self.name, "ResA",time.time()))Rlock.release()Rlock.release()if __name__ == "__main__":print("start---------------------------%s"%time.time())for i in range(0, 10):my_thread = MyThread()my_thread.start()
4 Event對(duì)象
線程的一個(gè)關(guān)鍵特性是每個(gè)線程都是獨(dú)立運(yùn)行且狀態(tài)不可預(yù)測(cè)。如果程序中的其他線程需要通過判斷某個(gè)線程的狀態(tài)來確定自己下一步的操作,這時(shí)線程同步問題就會(huì)變得棘手,為了解決這些問題,我們需要使用threading庫中的Event對(duì)象。對(duì)象包含一個(gè)可由線程設(shè)置的信號(hào)標(biāo)志,他允許線程等待某些事件的發(fā)生。
在初始情況下,Event對(duì)象中的信號(hào)標(biāo)志被設(shè)置為假。如果有線程等待一個(gè)Event對(duì)象,而這個(gè)Event對(duì)象的標(biāo)志為假,那么這個(gè)線程將會(huì)被一直阻塞直至該標(biāo)志為真。一個(gè)線程如果將一個(gè)Event對(duì)象的信號(hào)標(biāo)志設(shè)置為真,它將喚醒所有等待這個(gè)Event對(duì)象的線程。如果一個(gè)線程等待一個(gè)已經(jīng)被設(shè)置為真的Event對(duì)象,那么他將忽略這個(gè)事件,繼續(xù)執(zhí)行
event對(duì)象 線程之間的簡(jiǎn)單通信 event.set()更改標(biāo)志位為True, 所有阻塞池的線程激活進(jìn)入就緒狀態(tài), 等待操作系統(tǒng)調(diào)度 event.wait()等待;還可以接收一個(gè)超時(shí)參數(shù),默認(rèn)情況下超過這個(gè)參數(shù)設(shè)定的值之后,wait方法會(huì)返回 event.clear()恢復(fù)event的狀態(tài)值為False event.isSet():返回event的狀態(tài)值
右邊的線程需要左邊線程運(yùn)行的時(shí)候,用Event.set來更改狀態(tài),為True;
如果右邊的線程狀態(tài)一直為默認(rèn)為假,左邊的線程則一直等待,不會(huì)執(zhí)行。
應(yīng)用場(chǎng)景:
我們有多個(gè)線程從Redis隊(duì)列中讀取數(shù)據(jù)來處理,這些線程都要嘗試去連接Redis的服務(wù),一般情況下,如果Redis連接不成功,在各個(gè)線程的代碼中,都會(huì)去嘗試重新連接。如果我們想要在啟動(dòng)時(shí)確保Redis服務(wù)正常,才讓那些工作線程去連接Redis服務(wù)器,那么我們就可以采用threading.Event機(jī)制來協(xié)調(diào)各個(gè)工作線程的連接操作:主線程中會(huì)去嘗試連接Redis服務(wù),如果正常的話,觸發(fā)事件,各工作線程會(huì)嘗試連接Redis服務(wù)。
import threading import time import logginglogging.basicConfig(level=logging.DEBUG, format='(%(threadName)-10s) %(message)s',)def worker(event):logging.debug('Waiting for redis ready...')event.wait()#由于狀態(tài)默認(rèn)為假,需等待logging.debug('redis ready, and connect to redis server and do some work [%s]', time.ctime())time.sleep(1)def main():#主函數(shù)readis_ready = threading.Event()t1 = threading.Thread(target=worker, args=(readis_ready,), name='t1')#產(chǎn)生一個(gè)對(duì)象t1.start()#開始運(yùn)行t2 = threading.Thread(target=worker, args=(readis_ready,), name='t2')t2.start()#產(chǎn)生一個(gè)對(duì)象logging.debug('first of all, check redis server, make sure it is OK, and then trigger the redis ready event')time.sleep(3) # simulate the check progressreadis_ready.set()#更改狀態(tài)為True,默認(rèn)為Falseif __name__=="__main__":main()
5 Semaphore(信號(hào)量)
Semaphore管理一個(gè)內(nèi)置的計(jì)數(shù)器,
每當(dāng)調(diào)用acquire()時(shí)內(nèi)置計(jì)數(shù)器-1;
調(diào)用release() 時(shí)內(nèi)置計(jì)數(shù)器+1;
計(jì)數(shù)器不能小于0;當(dāng)計(jì)數(shù)器為0時(shí),acquire()將阻塞線程直到其他線程調(diào)用release()。
?
實(shí)例:(同時(shí)只有5個(gè)線程可以獲得semaphore,即可以限制最大連接數(shù)為5):
import threading import time semaphore = threading.Semaphore(5) def func():semaphore.acquire()print (threading.currentThread().getName() + ' get semaphore')time.sleep(2)semaphore.release() for i in range(20):#總數(shù)為20,最大連接為5,分為4次t1 = threading.Thread(target=func)t1.start()
轉(zhuǎn)載于:https://www.cnblogs.com/asaka/p/6846991.html
總結(jié)
以上是生活随笔為你收集整理的python之进程和线程2的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 一次上机试题(面向对象)
- 下一篇: 【二分图】洛谷P1640连续攻击游戏