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