目錄

什么是線程?
并發與并行
什么是GIL?
為什么要使用多線程?
多線程實例
守護線程
多線程共享全局變量
互斥鎖Lock
RLock遞歸鎖
信號量
python多線程是一個非常重要的知識點,接下來對該部分內容進行梳理。
什么是線程?線程也叫輕量級進程,是操作系統能夠進行運算調度的最小單位,它被包涵在進程之中,是進程中的實際運作單位。線程自己不擁有系統資源,只擁有一點兒在運行中必不可少的資源,但它可與同屬一個進程的其他線程共享進程所擁有的全部資源。一個線程可以創建和撤銷另一個線程,同一個進程中的多個線程之間可以并發執行。
并發與并行并發是快速交替輪換執行(線程看起來是同時在運行,實際上是一個線程遇到耗時的IO等操作時將此線程掛起釋放GIL全局解釋器鎖轉而執行其他線程的過程,這個過程輪換的非常快,所以宏觀上看起來就像是多個線程同時運行),并行是同一時間同時運行(進程可以做到并行運行,充分利用多核cpu的優勢)
什么是GIL?GIL全稱是全局解釋器鎖,為了解決多線程之間數據完整性和狀態同步而產生的一把鎖。它保證了在同一時刻只有一個線程執行,當其他線程需要運行時必須先拿到這把鎖才能執行,這就保證了數據的完整性和狀態的同步。關于GIL的更多知識可以參考這篇博客。
為什么要使用多線程?線程在程序中是獨立的、并發的執行流。與分隔的進程相比,進程中線程之間的隔離程度要小,它們共享內存、文件句柄?和其他進程應有的狀態。
因為線程的劃分尺度小于進程,使得多線程程序的并發性高。進程在執行過程之中擁有獨立的內存單元,而多個線程共享內存,從而極大的提升了程序的運行效率。線程比進程具有更高的性能,這是由于同一個進程中的線程都有共性,多個線程共享一個進程的虛擬空間。線程的共享環境包括進程代碼段、進程的共有數據等,利用這些共享的數據,線程之間很容易實現通信。操作系統在創建進程時,必須為改進程分配獨立的內存空間,并分配大量的相關資源,但創建線程則簡單得多。因此,使用多線程來實現并發比使用多進程的性能高得要多。
操作系統在創建進程時,需要為該進程重新分配系統資源,但創建線程的代價則小得多。因此使用多線程來實現多任務并發執行比使用多進程的效率。python語言內置了多線程功能支持,而不是單純地作為底層操作系統的調度方式,從而簡化了python的多線程編程。
多線程實例普通創建方式:

打印結果如下:

通過打印的順序可以發現,多線程是異步非阻塞的(首先打印task2,再打印task3,最后等待task1執行結束后再結束主線程),主線程的結束并沒有強制結束子線程,是等待子線程結束后才結束主線程。?當我們需要某個線程執行完成再往下面執行,可以手動添加join()方法阻塞主線程,這樣主線程會等待join的線程執行完成再往下執行,下面守護線程會講到join()方法。
自定義線程------繼承threading.Thread類實現自定義線程,其本質是重構Thread類的run方法:

打印結果:

t.start()方法在執行的時候會自動調用run()方法,所以在自定義線程的時候只需要重寫__init__方法和run()方法即可,__init__方法負責接收參數,這些參數最后會傳入run()方法中,根據需要實現自定義功能。
守護線程這里使用setDaemon(True)把所有的子線程都變成了主線程的守護線程,因此當主線程結束后,子線程也會隨之結束,所以當主線程結束后,整個程序就退出了。

把線程t1設置成守護線程,意味著不管t1線程有沒有運行完成,只要主線程運行結束了,t1線程也會被迫結束,看一下打印結果:

t2、t3線程運行結束后主線程也隨之運行到結尾,此時t1線程還處于sleep狀態,但是由于設置成守護線程了,此時主線程關閉,t1線程也被迫結束,整個程序退出。
但是在這種情況下,可以使用join()方法手動阻塞當前線程,看如下代碼:

使用t1.join()阻塞當前進程,當主線程運行到t1.join()的時候會被阻塞在此處,直到t1線程運行結束,所以就算t1線程設置成了守護線程,主線程也得等到t1線程運行完成后才能往下執行,然后結束主線程,退出程序。打印結果如下:

一個進程中的多個線程資源是共享的,看下面這個例子:

打印結果如下:

從打印結果可以看出來,全局的變量在work1中做了加法,在work2中打印num的值時顯示的是加法運行之后的值,說明兩個work操作的是同一個變量。從而印證了線程之間的資源是共享的。
雖然有了全局解釋器鎖GIL,但是它不能保證線程數據的安全性,當多個線程操作一個變量的時候,舉個例子有個變量a初始值是1,要對其進行a+=1這個操作可以細分成兩步,a+1 和a=a+1,比如兩個線程對a進行操作,當線程1拿到調度權的時候,對a進行a+1操作,此時線程1交出了調度權,線程2獲取到的執行的機會,同樣對a進行操作,此時的a仍然是1,因為線程1只是做了加法運算還沒有最終把a+1的值賦值給變量a的時候線程2便拿到了調度權,所以線程2做了一次完整的a+=1操作,a的值變成了2,這個時候線程2交出調度權,線程1拿到調度權繼續運行最后的賦值操作,a的值為2。此時發現a做了兩次加法操作,但是最終的值是2,這便產生了臟數據。為了解決這個問題引入線程鎖。
互斥鎖Lock為了解決線程數據不安全性,引入互斥鎖概念,看如下例子:

在修改變量n的時候,先lock.acquire()給線程上鎖,這樣其他的線程便拿不到這個資源,當完成對數據的操作后,lock.release()解鎖,其他線程便可以操作變量n,這樣便解決了原子性問題。
打印結果:使用10個線程對數字10000000進行減法操作,如期得到最后的結果1。使用互斥鎖解決了線程數據不安全問題,避免了臟數據的產生。

還有一種鎖RLock遞歸鎖,用法與lock鎖一樣,不同的是它支持嵌套,在多個鎖沒有釋放的時候,一般會使用RLock類。
信號量用一句話說,信號量就是為了控制線程的并發數。
例如餐桌有三個位置,但是有10個人同時就餐,這時候就需要信號量參與保證餐桌上只有三個人,也就是線程的并發數始終是3。

觀察打印結果發現,只有三個線程在并發運行,其他線程需要等待線程釋放之后才參與進來。線程池也是為了控制線程的并發數。

你是否還在尋找穩定的海外服務器提供商?創新互聯www.cdcxhl.cn海外機房具備T級流量清洗系統配攻擊溯源,準確流量調度確保服務器高可用性,企業級服務器適合批量采購,新人活動首月15元起,快前往官網查看詳情吧
當前文章:python多線程講解-創新互聯
文章URL:http://www.yijiale78.com/article20/dpcgco.html
成都網站建設公司_創新互聯,為您提供品牌網站設計、品牌網站建設、品牌網站制作、關鍵詞優化、云服務器、動態網站
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯