Python-GIL 進程池 線程池

    五、GIL vs 互斥鎖(*****)        一、什麼是GIL(Global  Interpreter  Lock)            GIL是全局解釋器鎖,是加到解釋器身上的,保護的就是解釋器級別的數據 (好比垃圾回收的數據)            同一個進程內的全部線程都須要先搶到GIL鎖,才能執行解釋器代碼        2 爲何須要GIL          python 中內存管理依賴於 GC(一段用於回收內存的代碼) 也須要一個線程            除了你本身開的線程 系統還有一些內置線程   就算你的代碼不會去競爭解釋器  內置線程也可能會競爭            因此必須加上鎖        三、GIL的影響            GIl會限制同一進程的內的多個線程同一時間只能有一個運行,也就是說python一個進程內的多線線程    沒法實現並行的效果,即沒法利用多核優點            而後多核提供的優點是同一時刻有多個cpu參與計算,意味着計算性能地提高,也就是說咱們的任務是            計算密集型的狀況下才須要考慮利用多核優點,此時應該開啓python的多進程            在咱們的任務是IO密集型的狀況下,再多的cpu對性能的提高也用處不大,也就說多核優點在IO密集型程序面前            發揮的做用微乎其微,此時用python的多線程也是能夠的        GIL的優缺點:            優勢:                保證Cpython解釋器內存管理的線程安全            缺點:                同一進程內全部的線程同一時刻只能有一個執行,沒法利用多核CPU                也就說Cpython解釋器的多線程沒法實現並行        (問題: 一個py程序 要想運行 必須運行解釋器 解釋器的工做時翻譯代碼 並執行           當一個py進程中 有多個線程 線程的任務就是執行代碼  意味者 多個線程都要使用解釋器           簡單的說 多線程會爭搶解釋器的執行權           若是是本身開的線程  多線程要訪問相同數據 加鎖就能解決           可是有一寫代碼不是程序員寫的 也確實須要共享使用 就是解釋器        GC:垃圾回收器  負責清理內存中的無用數據 清理垃圾也須要執行代碼  可是GC不該該卡住用戶的代碼執行            只能開線程            GC 看到 x = 10   x = 1   準備刪除10  這時候忽然CPU切到用戶線程  a = 10  此此時尚未問題            緊接着 CPU 又切到GC  GC上來就刪除10  在切到用戶線程 a 所指向的地址被清理了 產生錯誤        解決方案: 給解釋器加上鎖  保證GC執行期間 用戶線程不能執行)        四、GIL vs 自定義鎖            保護不一樣的數據就應該加不一樣的鎖。            相同點:都是互斥鎖            不一樣點:                 GIL解釋器級別鎖 鎖的是解釋器代碼                 自定義鎖 鎖的是本身寫的代碼            GIL 在當一個線程調用解釋器時 自動加鎖  在IO阻塞時或線程代碼執行完畢/執行時間過長3ms時 自動解鎖            本質就是一個互斥鎖,而後保護不一樣的數據就應該用不一樣的互斥鎖,保護咱們應用程序級別的數據必須自定義互斥鎖            有了GIL 爲何還須要自定義鎖?                GIL 不清楚什麼代碼會形成數據競爭問題  不知道什麼地方該加    6 Cpython的解釋器下,多線程是雞肋?*****        多個任務是IO密集型:多線程  (IO的速度 明顯要比CPU執行速度慢)        多個任務是計算密集型:多進程    七、死鎖現象與遞歸鎖(可重入鎖),信號量(**)      死鎖?        進程也有死鎖與遞歸鎖,在進程那裏忘記說了,放到這裏一切說了額        所謂死鎖: 是指兩個或兩個以上的進程或線程在執行過程當中,因爭奪資源而形成的一種互相等待的現象,                若無外力做用,它們都將沒法推動下去。此時稱系統處於死鎖狀態或系統產生了死鎖,                這些永遠在互相等待的進程稱爲死鎖進程,以下就是死鎖        解決方法,遞歸鎖,在Python中爲了支持在同一線程中屢次請求同一資源,python提供了可重入鎖RLock。            這個RLock內部維護着一個Lock和一個counter變量,counter記錄了acquire的次數,            從而使得資源能夠被屢次require。直到一個線程全部的acquire都被release,            其餘的線程才能得到資源。上面的例子若是使用RLock代替Lock,則不會發生死鎖:        mutexA=mutexB=threading.RLock() #一個線程拿到鎖,counter加1,該線程內又碰到加鎖的狀況,        則counter繼續加1,這期間全部其餘線程都只能等待,等待該線程釋放全部鎖,即counter遞減到0爲止        死鎖形成的問題.程序卡死        一個鎖不會產生死鎖        當有多個鎖多個線程時會產生死鎖        a b  鎖        p k  線程        當p 和 k 都須要a和b鎖時纔可能產生死鎖      遞歸鎖(可重入鎖)RLock        同一個線程能夠屢次執行acquire  執行一次acquire  計數加1        執行一次release 次數減一 執行acquire的次數須要與release的次數對應        在執行被鎖的代碼時   同一個線程 不會判斷次數   其餘線程須要判斷 計數爲0才能夠執行        不是用來解決死鎖的      Semaphore信號量(瞭解) 經常使用在線程中        信號量做用:限制同時執行被鎖代碼的線程數量        案列:        sem = semaphore(2)        acquire        code.....        release        開了十個線程 只能有兩個同時執行    八、隊列queue(***)        queue 這個queue和進程裏的Queue不一樣 就是一個簡單的容器            隊列是一種數據的容器            特色:先進先出        queue先進先出        lifoqueue先進後出        priorityqueue  優先級隊列  整型表示優先級   數字越大優先級越低        import queue        q = queue.Queue()# 普通隊列 先進先出        q.put("a")        q2 = queue.LifoQueue()# 堆棧隊列  先進後出 後進先出  函數調用就是進棧  函數結束就出棧 遞歸形成棧溢出        q3 = queue.PriorityQueue()  # 優先級隊列    九、Event事件(**)瞭解    python線程的事件用於主線程控制其餘線程的執行,事件主要提供了三個方法 set、wait、clear。    事件處理的機制:全局定義了一個「Flag」,若是「Flag」值爲 False,        那麼當程序執行 event.wait 方法時就會阻塞,若是「Flag」值爲True,那麼event.wait 方法時便再也不阻塞。        是什麼?            線程間通信的方式        爲何用?            簡化代碼        set()設置爲True        wati()阻塞 直到爲True        clear:將「Flag」設置爲False四、池(*****)    就是一個裝進程/線程的容器    爲什麼要用池:        操做系統沒法無限開啓進程或線程        池做用是將進程或線程控制操做系統可承受的範圍內    何時用進程池: (好比 雙十一)        當程序中有多個進程時 管理變得很是麻煩        進程池能夠幫咱們管理進程            1.進程的建立            2.進程的銷燬            3.任務的分配            4.限制最大的進程數 保證系統正常運行    池內裝的東西有兩種:            裝進程:進程池            裝線程:線程池    進程池        在利用Python進行系統管理的時候,特別是同時操做多個文件目錄,或者遠程控制多臺主機,        並行操做能夠節約大量的時間。多進程是實現併發的手段之一,須要注意的問題是:            1 很明顯須要併發執行的任務一般要遠大於核數            2 一個操做系統不可能無限開啓進程,一般有幾個核就開幾個進程            3 進程開啓過多,效率反而會降低(開啓進程是須要佔用系統資源的,並且開啓多餘核數目的進程也沒法作到並行)        例如當被操做對象數目不大時,能夠直接利用multiprocessing中的Process動態成生多個進程,        十幾個還好,但若是是上百個,上千個。。。手動的去限制進程數量卻又太過繁瑣,此時能夠發揮進程池的功效。        咱們就能夠經過維護一個進程池來控制進程數目,好比httpd的進程模式,規定最小進程數和最大進程數...        建立進程池的類:若是指定numprocess爲3,則進程池會從無到有建立三個進程,                    而後自始至終使用這三個進程去執行全部任務,不會開啓其餘進程    使用方式?        ThreadPoolExecutor  線程池            實例化 時指定最大線程數        ProcessPoolExecutor  進程池            實例化 時指定最大進程數        執行submit來提交任務        from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor        p=ThreadPoolExecutor(4) # 默認開啓的線程數是cpu的核數*5        p.submit(task,i)    總結一下:        進程池能夠自動建立進程        進程限制最大進程數        自動選擇一個空閒的進程幫你處理任務    進程何時算是空閒?        代碼執行完算是空閒    進程池,池子內何時裝進程:併發的任務屬於計算密集型    線程池,池子內何時裝線程:併發的任務屬於IO密集型回調函數:    須要回調函數的場景:進程池中任何一個任務一旦處理完了,就當即告知主進程:        我好了額,你能夠處理個人結果了。主進程則調用一個函數去處理該結果,該函數即回調函數    咱們能夠把耗時間(阻塞)的任務放到進程池中,而後指定回調函數(主進程負責執行),    這樣主進程在執行回調函數時就省去了I/O的過程,直接拿到的是任務的結果。    若是在主進程中等待進程池中全部任務都執行完畢後,再統一處理結果,則無需回調函數
相關文章
相關標籤/搜索