1. 程式人生 > 其它 >對程式-程序-執行緒 的初步認識

對程式-程序-執行緒 的初步認識

# 什麼是程式?


一般意義上的程式可以認為是在特定作業系統上的可執行檔案。也就是原始碼經過編譯、連結而形成的可執行檔案,它依賴於執行的作業系統,因為正是作業系統提供了該程式執行的環境(執行庫等)。它是一個靜態的概念。

# 什麼是程序?


程序,是一個動態的概念,它有自己的地址空間,能執行一些操作。程式的執行都會伴隨著程序的生成,一個程式的執行會產生一個或多個程序。
所以可認為程序是程式的動態概念。

# 什麼是執行緒?


執行緒有時候又被稱為輕量級程序,是程式執行的最小單元。和上文中一樣的,一個程序可對應多個執行緒,而一個執行緒只屬於一個程序。
程序的執行是以執行緒為單位進行得,比如說一個簡單“hello world”程式只有一個執行緒,就是main()函式對應的執行緒。

執行緒的構成

1)執行緒ID。用於標實執行緒

2)當前指令指標PC。標明下一指令執行點

3)暫存器集合和堆疊。該執行緒的可用空間

多執行緒

大多數軟體應用中,執行緒的數量都不止一個。多執行緒可用併發的執行,並共享程序的全域性便來那個和堆的資料。

多執行緒優勢:

1)某個操作可能會陷入長時間的等待。採用多執行緒,當一個執行緒等待的時候,可執行其他執行緒,充分利用cpu。

2)某操作(如計算)可能會消耗大量時間,而導致和使用者之間的互動中斷。多執行緒可讓一個執行緒負責計算,另一執行緒負責互動。

3)軟體本身就要求併發操作,如多端下載軟體

4)多核cpu,本身就具備同時執行多個執行緒的能力

執行緒訪問許可權

一般來說執行緒能訪問程序記憶體中的所有資料,但實際應用中執行緒也有自己的空間
1)棧(可能被其他程序訪問,但仍可認為是私有資料)

2)執行緒區域性儲存,一般只有很小容量

3)暫存器(包括PC暫存器)

執行緒排程

最好的情況是:當處理器數量大於要處理的執行緒數目的時候,所有執行緒都可以同時執行。實現真正意義上的併發。

而這種情況在現實中基本不可能。現實中的併發只是一種模擬出來的狀態,特別是在單核處理對於多執行緒的時候。它通過讓多個執行緒交替執行,每個執行緒執行很短時間,從表面上看,這些執行緒同時執行,實現併發。

每個執行緒都想被執行,但是每次執行的執行緒數量是有限的,所以就要有一種方法來從眾多的執行緒中選出要執行的執行緒,現在討論下單核的情況,多核的類似。

在作業系統中有專門的執行緒排程演算法來實現,下面列幾個簡單的“排程演算法”

先進先出

所有的執行緒組成一個佇列,新生的執行緒加入到佇列末尾,每次取隊頭執行。有一個缺陷就是,如果新生成的執行緒是緊急操作,需要作業系統儘快相應,這種排程方法就不能滿足了。

優先順序排程

每個執行緒都有自己的優先順序,並且是可以被作業系統修改的,排程時候每次選取優先順序最高的執行。這種方法彌補了上一種方法的缺陷。對於需要及時相應的緊急事件,可以給他一個高優先順序,這樣就能在下次被排程。然而這種方法也有一個問題,也就是所謂的飢餓。如果某執行緒“看似”無關緊要,被給予一個低得優先順序,以後每次產生的執行緒優先順序都比他高,那麼這個執行緒會一直得不到執行,成為餓死。一個解決的辦法是隨之事件的推移而提升執行緒的優先順序。這樣只要事件足夠長,低優先順序的執行緒也會獲得高優先順序而被執行。
從上面的分析來開,執行緒似乎有兩個狀態:執行和不執行(等待)。其實作業系統中的每個執行緒都對應三個狀態。

執行緒的狀態:

上面說了執行緒排程的問題,只有準備就緒的執行緒(這種執行緒稱為就緒的執行緒),才能被排程。排程以後,執行緒就在處理器中被執行,這時執行緒的狀態為執行時。如果該執行緒在等待某種事件的發生(如響應I/O),這種狀態成為等待。

總結下執行緒的三種狀態:

1)就緒:此時執行緒可以立刻執行i(如果該執行緒被呼叫的話)

2)執行:此時執行緒正在執行

3)等待:執行緒正在等待某件事的發生以便繼續執行

可搶佔執行緒和不可搶佔執行緒

可搶佔執行緒就是說,在該執行緒用完自己的時間片以後,作業系統會強制把該執行緒切出,以便執行其他執行緒。
而不可搶佔執行緒則是執行緒不能強制切出,除非他自己放棄cpu的使用權而終止執行緒,而不是靠時間片的用盡而強制切出。不可搶佔執行緒的執行緒切換時間是確定的,當該執行緒自願切出時發生。

執行緒安全

多執行緒併發時,在訪問資料方面會出現一些問題。特別是當多個執行緒訪問同一個變數的時候。

下面將用一個例子來說明可能出現的問題:

執行緒A、執行緒B都對變數X進行操作,操作順序如下:

1)執行緒A對X賦值

2)執行緒A對X自加

3)執行緒B使用X的值(比如說把它賦值給另一個變數)

程式碼經過編譯之後,在處理器中執行程式碼時,通常一個很簡單的運算(如自家運算)都會被分為多個步驟執行(指令流水)。

比如當執行緒A對X自加運算的時候,編譯後的自加運算共分為三步,當沒有執行完這三步的時候,可能執行緒A就會被切出(比如說有需要即時響應的操作發生)。也就是說執行緒A在對資料還沒處理完全的時候被切出了,這樣當執行緒B執行的時候,使用的X值將不是我們期望的值,顯然,發生了錯誤。

解決策略:

上面問題的出現本質上是因為一個不應該被打斷的操作被強行中斷了,那麼有一種解決的辦法就是設定一種規定一些操作,在執行的時候不能被中斷。這樣就避免了操作還沒完成就被換出的情況。把這些簡單的操作稱為原子的操作。windows中對於這種操作也有支援。

但是這種策略只適用於簡單的情況。對於複雜的情況,我們用一種稱為同步與鎖的機制來實現。

文章參考自:我認識的執行緒