操作系統為了程序的並發執行引入了進程的概念,提高了資源的利用率以及吞吐量。
在20世紀 60年代人們提出了進程的概念后,在OS中一直都是以進程作為能擁有資源和獨立運行的基本單位的。
直到 20 世紀 80 年代中期,人們又提出了比進程更小的能獨立運行的基本單位——線程(Threads)
試圖用它來提高系統內程序並發執行的程度,從而可進一步提高系統的吞吐量。
簡言之,進程的概念,使之能夠並發執行多道程序,線程的概念讓你更好地並發執行程序,一個是能不能的問題,一個是更好的問題。
線程與進程對比
線程概念的發展
進程概念提出的目的就是為了多道程序並發執行,並發過程中必然意味着不斷地進程調度任務切換,但是他又是資源分配的獨立單位,也就是說他要背着資源來回跑。
舉個例子:
辦公室內,每個人都有一台電腦,電腦就是資源
然后大家經常需要不斷地變換座位位置(比如大家都是哪里需要去哪里,客服缺人了,銷售就頂一個過去)
每個人都抱着自己的電腦來回的換位置方便?還是大家只是人員走動,電腦就使用那個位置的電腦方便?
進程也是有些類似的道理,你帶着這么多資源來回切換調度,必然會帶來更多的時&&空開銷。
所以創建了線程的概念,程序運行時所需的資源和程序的調度進行解耦
進程仍舊負責資源的獨立分配,但是線程作為調度運行的獨立單位,僅僅攜帶自身運行的必備的一丁點資源。
對比
線程具有許多傳統進程所具有的特征,所以又稱為輕型進程(Light-Weight Process)或進程元
相應地把傳統進程稱為重型進程(Heavy-Weight Process),傳統進程相當於只有一個線程的任務。
在引入了線程的操作系統中,通常一個進程都擁有若干個線程,至少也有一個線程。
並發性
傳統的OS系統,進程之間可以並發執行,引入線程概念的OS,不僅僅進程間可以並發執行,一個進程中的線程也可以並發執行,不同進程中的線程也可以並發執行
獨立性
同一進程中的多個線程獨立性比不同進程間的獨立性差很多。
每個進程都是獨立的地址空間和資源,同一進程下多線程他們共享進程下的資源,而且通常他們往往是用來相互合作的,每個線程都可以訪問所在進程的所有地址空間,比如一個線程打開的文件,可以被其他線程讀寫。
調度性
傳統OS,進程作為資源分配和調度分派的基本單位,進程是可以獨立運行的基本單位,不過進程調度切換時空開銷大
引入線程的OS,線程是運行調度和分派的基本單位,線程才是獨立運行的基本單位,線程切換時,僅僅需要保存和設置少量寄存器內容,代價遠遠小於進程切換,不過需要注意是同一個進程內線程切換不會進程切換,但是不同進程中的線程進程切換,仍舊會導致進程切換。
擁有資源
進程擁有資源,並且作為系統中擁有資源的獨立基本單位。
線程自身不擁有系統資源,僅僅擁有一點必不可少的,獨立運行需要的資源,比如線程中的TCB。
除了自身的丁點兒資源外,共享所屬進程的資源,同一個進程下所有線程,擁有相同的地址空間。
多處理器支持
傳統進程(或者說單線程進程)只能運行於一個處理機上,不管有多少個處理機;
但是對於多線程進程,就可以將一個進程中的多個線程分配到多個處理機上,並行運行
簡言之,多線程可以讓多核CPU充分發揮性能並行運行。
系統開銷
進程和線程的創建撤銷,系統都要為止分配和回收資源,比如內存空間、IO設備等,進程和線程的上下文切換,系統也都需要付出一定的時空開銷。
但是,線程相關的開銷明顯小於進程。
線程簡介
各線程之間也是存在資源共享和相互合作的,線程在運行時也是間斷的,輪轉切換的。
線程也是有運行狀態的,這一點與進程並沒有本質區別,最主要的狀態也是就緒、執行、阻塞
進程的控制核心信息保存在PCB中,線程也有對應的組成---TCB,所有用於控制和管理線程的信息都保存在TCB中
線程盡管是另外一種完全不同的事物,但是畢竟是從進程的概念演化而來,也是操作系統對程序運行抽象的一部分,所以,線程必然與進程有着很多的相似點
線程實現
線程的實現主要有三種形式
- 內核支持
- 用戶級線程
- 另外就是二者的組合

從上面的分析中可以看得出來,內核支持和用戶級都有各自明顯的缺點和優點。
有些操作系統把用戶級線程和內核支持線程兩種方式進行組合,提供了組合方式ULT/KST 線程。
在組合方式線程系統中, 內核支持多KST線程的建立、調度和管理,同時,也允許用戶應用程序建立、調度和管理用戶級線程。
一些內核支持線程對應多個用戶級線程,程序員可按應用需要和機器配置對內核支持線程數目進行調整,以達到較好的效果。
組合方式線程中,同一個進程內的多個線程可以同時在多處理器上並行執行,而且在阻塞一個線程時,並不需要將整個進程阻塞。
所以,組合方式多線程機制能夠結合 KST和 ULT兩者的優點,並克服了其各自的不足。
線程的同步與通信
關於進程的同步與通信的相關邏輯原理,對於進程的同步與通信絕大多數都是適用的。
針對於這些原理,多線程OS也提供了多種同步機制,如互斥鎖、條件變量、計數信號量以及多讀、單寫鎖等。
信號量機制
進程中的信號量機制完全適合多線程同步
根據用法分為兩種
- 私用信號量(private samephore)
- 公用信號量(public semaphort)
系統運行中,有多個進程,進程中又有多個線程。
如果是為了同一進程中多個線程同步設置的信號量,量屬於特定的進程所有,這就叫做私用,OS並不知道私用信號量的存在。
如果是為了不同進程或者不同進程中的線程之間而設置的,就叫做公用。其數據結構是存放在受保護的系統存儲區中,由OS為它分配空間並進行管理,故也稱為系統信號量。
互斥鎖(mutex)
互斥鎖是一種比較簡單的、用於實現線程間對資源互斥訪問的機制
互斥鎖可以有兩種狀態
- 開鎖(unlock)
- 關鎖(lock)
當一個線程需要讀/寫一個共享數據段時,需要對mutex進行上鎖,離開時需要解鎖。
上鎖時,首先校驗 mutex 的狀態,如果它已處於關鎖狀態,則試圖訪問該數據段的線程將被阻塞;如果 mutex處於開鎖狀態,則將 mutex 上鎖后便去讀/寫該數據段。
線程完成操作后,必須將 mutex 解鎖,同時還需要將阻塞在該互斥鎖上的一個線程喚醒,其它的線程仍被阻塞在等待mutex打開的隊列上。
另外,為了減少線程被阻塞的機會,在有的系統中還提供了一種用於 mutex 上的操作命令 Trylock。
顧名思義,並不會因為無法進入而阻塞,若 mutex 處於上鎖狀態,則 Trylock 並不會阻塞該線程,而只是返回一個指示操作失敗的狀態碼。
條件變量
在許多情況下,只利用 mutex 來實現互斥訪問可能會引起死鎖,比如A線程請求資源順序為R1,R2,B線程請求資源順序為R2,R1
如果A對mutex 1上鎖成功進入臨界區后,需要獲取R2的鎖mutex 2,可是此時B獲得了資源R2,對mutex 2已經上鎖,此時,A等待mutex 2 B等待mutex 1,形成了死鎖
所以說,鎖,應該是僅僅用於在條件成立時進行操作時的一個同步保障,而不能在整個過程中都依靠鎖
可以借助於條件變量,就是條件
每一個條件變量通常都與一個互斥鎖一起使用,單純的互斥鎖用於短期鎖定,主要是用來保證對臨界區的互斥進入。
而條件變量則用於線程的長期等待,直至所等待的資源成為可用的資源。
申請
Lock mutex
while (條件狀態不滿足) {
wait(condition variable);//釋放鎖,線程掛起等待,直到條件滿足通知;
}
臨界區其他操作
unlock mutex;
釋放
Lock mutex
一些操作
unlock mutex;
wakeup(condition variable);
簡言之,借助於條件變量用於控制長時間的等待,鎖用於控制對資源的同步。
總結
本文對線程進行了非常簡單的介紹,線程之於進程在很多的方面有着極其類似的邏輯,尤其是從調度的視角看。
畢竟線程就是對進程中關於調度部分的獨立抽象。
只要能夠理解進程和線程的目的就能夠很好地理解他們相似的原因,因為都是操作系統對於程序運行的抽象描述,線程是進程的更加細粒度的掌控。
在換句話說就是操作系統的角度對程序的執行抽象為:“資源的分配”“調度”
最初這兩個概念都是加諸於進程這個概念上,后續為了更加高效將兩個概念進行了拆分,就是這樣
所以說,對於原先介紹的進程的相關概念中關於調度部分的絕大多數理論,都是適用於線程概念的