天天看點

三 python并發程式設計之多線程-理論

  • ​​一 什麼是線程  ​​
  • ​​二 線程的建立開銷小​​
  • ​​三 線程與程序的差別​​
  • ​​四 為何要用多線程​​
  • ​​五 多線程的應用舉例​​
  • ​​六 經典的線程模型(了解)​​
  • ​​七 POSIX線程(了解)​​
  • ​​八 在使用者空間實作的線程(了解)​​
  • ​​九 在核心空間實作的線程(了解)​​
  • ​​十 使用者級與核心級線程的對比(了解)​​
  • ​​十一 混合實作(了解)​​
  • ​​十二 線程小故事​​

一 什麼是線程

在傳統作業系統中,每個程序有一個位址空間,而且預設就有一個控制線程

是以,程序隻是用來把資源集中到一起(程序隻是一個資源機關,或者說資源集合),而線程才是cpu上的執行機關。

二 線程與程序的差別

程序:一個在記憶體中運作的應用程式。每個程序都有自己獨立的一塊記憶體空間,一個程序可以有多個線程,比如在Windows系統中,一個運作的xx.exe就是一個程序。

線程:程序中的一個執行任務(控制單元),負責目前程序中程式的執行。一個程序至少有一個線程,一個程序可以運作多個線程,多個線程可共享資料。

線程具有許多傳統程序所具有的特征,故又稱為輕型程序(Light—Weight Process)或程序元;而把傳統的程序稱為重型程序(Heavy—Weight Process),它相當于隻有一個線程的任務。在引入了線程的作業系統中,通常一個程序都有若幹個線程,至少包含一個線程。

根本差別:程序是作業系統資源配置設定的基本機關,而線程是處理器任務排程和執行的基本機關

資源開銷:每個程序都有獨立的代碼和資料空間(程式上下文),程式之間的切換會有較大的開銷;線程可以看做輕量級的程序,同一類線程共享代碼和資料空間,每個線程都有自己獨立的運作棧和程式計數器(PC),線程之間切換的開銷小。

包含關系:如果一個程序内有多個線程,則執行過程不是一條線的,而是多條線(線程)共同完成的;線程是程序的一部分,是以線程也被稱為輕權程序或者輕量級程序。

記憶體配置設定:同一程序的線程共享本程序的位址空間和資源,而程序之間的位址空間和資源是互相獨立的

影響關系:一個程序崩潰後,在保護模式下不會對其他程序産生影響,但是一個線程崩潰整個程序都死掉。是以多程序要比多線程健壯。

執行過程:每個獨立的程序有程式運作的入口、順序執行序列和程式出口。但是線程不能獨立執行,必須依存在應用程式中,由應用程式提供多個線程執行控制,兩者均可并發執行

三 什麼是多線程,多線程的優劣?

多線程:多線程是指程式中包含多個執行流,即在一個程式中可以同時運作多個不同的線程來執行不同的任務。

多線程的好處:

可以提高 CPU 的使用率。在多線程程式中,一個線程必須等待的時候,CPU 可以運作其它的線程而不是等待,這樣就大大提高了程式的效率。也就是說允許單個程式建立多個并行執行的線程來完成各自的任務。

多線程的劣勢:

線程也是程式,是以線程需要占用記憶體,線程越多占用記憶體也越多;

多線程需要協調和管理,是以需要 CPU 時間跟蹤線程;

線程之間對共享資源的通路會互相影響,必須解決競用共享資源的問題。

四 什麼是上下文切換?

多線程程式設計中一般線程的個數都大于 CPU 核心的個數,而一個 CPU 核心在任意時刻隻能被一個線程使用,為了讓這些線程都能得到有效執行,CPU 采取的政策是為每個線程配置設定時間片并輪轉的形式。當一個線程的時間片用完的時候就會重新處于就緒狀态讓給其他線程使用,這個過程就屬于一次上下文切換。

概括來說就是:目前任務在執行完 CPU 時間片切換到另一個任務之前會先儲存自己的狀态,以便下次再切換回這個任務時,可以再加載這個任務的狀态。任務從儲存到再加載的過程就是一次上下文切換。

上下文切換通常是計算密集型的。也就是說,它需要相當可觀的處理器時間,在每秒幾十上百次的切換中,每次切換都需要納秒量級的時間。是以,上下文切換對系統來說意味着消耗大量的 CPU 時間,事實上,可能是作業系統中時間消耗最大的操作。

Linux 相比與其他作業系統(包括其他類 Unix 系統)有很多的優點,其中有一項就是,其上下文切換和模式切換的時間消耗非常少。

五 守護線程和使用者線程有什麼差別呢?

使用者 (User) 線程:運作在前台,執行具體的任務,如程式的主線程、連接配接網絡的子線程等都是使用者線程

守護 (Daemon) 線程:運作在背景,為其他前台線程服務。也可以說守護線程是 JVM 中非守護線程的 “傭人”。一旦所有使用者線程都結束運作,守護線程會随 JVM 一起結束工作

main 函數所在的線程就是一個使用者線程啊,main 函數啟動的同時在 JVM 内部同時還啟動了好多守護線程,比如垃圾回收線程。

比較明顯的差別之一是使用者線程結束,JVM 退出,不管這個時候有沒有守護線程運作。而守護線程不會影響 JVM 的退出。

注意事項:

setDaemon(true)必須在start()方法前執行,否則會抛出 IllegalThreadStateException 異常

在守護線程中産生的新線程也是守護線程

不是所有的任務都可以配置設定給守護線程來執行,比如讀寫操作或者計算邏輯

守護 (Daemon) 線程中不能依靠 finally 塊的内容來確定執行關閉或清理資源的邏輯。因為我們上面也說過了一旦所有使用者線程都結束運作,守護線程會随 JVM 一起結束工作,是以守護 (Daemon) 線程中的 finally 語句塊可能無法被執行。

六 如何在 Windows 和 Linux 上查找哪個線程cpu使用率最高?

windows上面用任務管理器看,linux下可以用 top 這個工具看。

找出cpu耗用厲害的程序pid, 終端執行top指令,然後按下shift+p 查找出cpu利用最厲害的pid号

根據上面第一步拿到的pid号,top -H -p pid 。然後按下shift+p,查找出cpu使用率最厲害的線程号,比如top -H -p 1328

将擷取到的線程号轉換成16進制,去百度轉換一下就行

使用jstack工具将程序資訊列印輸出,jstack pid号 > /tmp/t.dat,比如jstack 31365 > /tmp/t.dat

編輯/tmp/t.dat檔案,查找線程号對應的資訊

七:什麼是線程死鎖

百度百科:死鎖是指兩個或兩個以上的程序(線程)在執行過程中,由于競争資源或者由于彼此通信而造成的一種阻塞的現象,若無外力作用,它們都将無法推進下去。此時稱系統處于死鎖狀态或系統産生了死鎖,這些永遠在互相等待的程序(線程)稱為死鎖程序(線程)。

多個線程同時被阻塞,它們中的一個或者全部都在等待某個資源被釋放。由于線程被無限期地阻塞,是以程式不可能正常終止。

如下圖所示,線程 A 持有資源 2,線程 B 持有資源 1,他們同時都想申請對方的資源,是以這兩個線程就會互相等待而進入死鎖狀态。

三 python并發程式設計之多線程-理論

形成死鎖的四個必要條件是什麼

  1. 互斥條件:線程(程序)對于所配置設定到的資源具有排它性,即一個資源隻能被一個線程(程序)占用,直到被該線程(程序)釋放
  2. 請求與保持條件:一個線程(程序)因請求被占用資源而發生阻塞時,對已獲得的資源保持不放。
  3. 不剝奪條件:線程(程序)已獲得的資源在末使用完之前不能被其他線程強行剝奪,隻有自己使用完畢後才釋放資源。
  4. 循環等待條件:當發生死鎖時,所等待的線程(程序)必定會形成一個環路(類似于死循環),造成永久阻塞

如何避免線程死鎖

我們隻要破壞産生死鎖的四個條件中的其中一個就可以了。

破壞互斥條件

這個條件我們沒有辦法破壞,因為我們用鎖本來就是想讓他們互斥的(臨界資源需要互斥通路)。

破壞請求與保持條件

一次性申請所有的資源。

破壞不剝奪條件

占用部分資源的線程進一步申請其他資源時,如果申請不到,可以主動釋放它占有的資源。

繼續閱讀