在理解多線程之前,我們先搞清楚什么是線程。根據(jù)維基百科的描述,線程是操作系統(tǒng)能夠進行運算調(diào)度的最小單位。它被包含在進程之中,是行程中的實際運行單位。一條線程指的是進程中一個單一順序的控制流,一個進程中可以并行多個線程,每條線程并行執(zhí)行不同的任務(wù)。每個線程共享堆空間,擁有自己獨立的??臻g。
這里反復出現(xiàn)的概念是線程和進程,我們在這里列出它們的區(qū)別:
多線程是指從軟件或者硬件上實現(xiàn)多個線程并發(fā)執(zhí)行的技術(shù)。具有多線程能力的計算機因有硬件支持而能夠在同一時間執(zhí)行多個線程,進而提升整體處理效能。
隨著計算機硬件的發(fā)展,多核CPU已經(jīng)屢見不鮮了,甚至手機處理器都早已是多核的天下。這就給我們使用多線程提供了硬件基礎(chǔ),但是,只是因為硬件讓我們可以實現(xiàn)多線程,就要這樣做嗎?一起來看看多線程的優(yōu)點:
前面夸了多線程的優(yōu)點,凡事都有兩面性,使用多線程也有弊端。盡管使用多線程往往可以獲得更大的吞吐率和更短的響應(yīng)時間,但是,多線程程序不一定比單線程程序執(zhí)行速度快。很多線程存在情況下,線程之間的切換會非常頻繁,切換帶來的性能損耗是非??捎^的。
先來解釋一下什么是上下文切換(context switch)。在多任務(wù)處理系統(tǒng)中,作業(yè)數(shù)通常大于CPU數(shù)。為了讓用戶覺得這些任務(wù)在同時進行,CPU給每個任務(wù)分配一定時間,把當前任務(wù)狀態(tài)保存下來,當前運行任務(wù)轉(zhuǎn)為就緒(或者掛起、刪除)狀態(tài),另一個被選定的就緒任務(wù)成為當前任務(wù)。之后CPU可以回過頭再處理之前被掛起任務(wù)。上下文切換就是這樣一個過程,它允許CPU記錄并恢復各種正在運行程序的狀態(tài),使它能夠完成切換操作。在這個過程中,CPU會停止處理當前運行的程序,并保存當前程序運行的具體位置以便之后繼續(xù)運行。
上下文切換在不同的場合有不同的含義,在下表中列出:
上下文切換種類 | 描述 |
---|---|
線程切換 | 同一進程中的兩個線程之間的切換 |
進程切換 | 兩個進程之間的切換 |
模式切換 | 在給定線程中,用戶模式和內(nèi)核模式的切換 |
地址空間切換 | 將虛擬內(nèi)存切換到物理內(nèi)存 |
根據(jù)種類的不同,切換時造成的性能消耗也不同。
究竟什么時候會發(fā)生上下文切換?總共有三種情況:
上下文切換發(fā)生條件 | 描述 |
---|---|
中斷處理 | 中斷分為硬件中斷和軟件中斷,軟件中斷包括因為IO阻塞、未搶到資源或者用戶代碼等原因,線程被掛起 |
多任務(wù)處理 | 每個程序都有相應(yīng)的處理時間片,當前任務(wù)的時間片用完之后,系統(tǒng)CPU正常調(diào)度下一個任務(wù) |
用戶態(tài)切換 | 這種情況下,上下文切換并非一定發(fā)生,只在特定操作系統(tǒng)才會發(fā)生上下文切換 |
為了理解為什么上下文切換的時候會損耗性能,我們應(yīng)該先看看上下文切換的過程中究竟發(fā)生了什么。在切換過程中,正在執(zhí)行的進程的狀態(tài)必須以某種方式存儲起來,這樣在未來才能被恢復。這里說的進程狀態(tài)包括該進程正在使用的所有寄存器(尤其是程序計數(shù)器),和一些必要的操作系統(tǒng)數(shù)據(jù)。保存進程狀態(tài)的數(shù)據(jù)結(jié)構(gòu)叫做“進程控制塊”(PCB,process control block);
PCB通常是系統(tǒng)內(nèi)存占用區(qū)中的一個連續(xù)存區(qū),它存放著操作系統(tǒng)用于描述進程情況及控制進程運行所需的全部信息,它使一個在多道程序環(huán)境下不能獨立運行的程序成為一個能獨立運行的基本單位或一個能與其他進程并發(fā)執(zhí)行的進程。
上下文切換的具體步驟是(假設(shè)當前進程是進程A,要切換到的下一個進程是進程B):
線程分為用戶級線程和內(nèi)核級線程。同一進程中的用戶級線程切換的時候,只需要保存用戶寄存器的內(nèi)容,程序計數(shù)器,棧指針,不需要模式切換。但是這樣會導致線程阻塞和無法利用多處理器。而同一進程中的內(nèi)核級線程切換的時候,就克服了這兩個缺點,但是除了保存上下文,還要進行模式切換。
線程切換和進程切換的步驟也不同。進程的上下文切換分為兩步:1.切換頁目錄以使用新的地址空間;2.切換內(nèi)核棧和硬件上下文。對于Linux來說,線程和進程的最大區(qū)別就在于地址空間。對于線程切換,第1步是不需要做的,第2是進程和線程切換都要做的。所以明顯是進程切換代價大。線程上下文切換和進程上下文切換一個最主要的區(qū)別是線程的切換虛擬內(nèi)存空間依然是相同的,但是進程切換是不同的。這兩種上下文切換的處理都是通過操作系統(tǒng)內(nèi)核來完成的。內(nèi)核的這種切換過程伴隨的最顯著的性能損耗是將寄存器中的內(nèi)容切換出。
根據(jù)前面的切換步驟,我們可以很容易得到性能損耗的原因。
上下文切換會導致CPU在寄存器和運行隊列之間來回奔波。這種消耗可以分為兩種
損耗種類 | 描述 |
---|---|
直接損耗 | CPU寄存器需要保存和加載, 系統(tǒng)調(diào)度器的代碼需要執(zhí)行, TLB實例需要重新加載, CPU 的pipeline需要刷掉 |
間接損耗 | 多核的cache之間得共享數(shù)據(jù) |
聯(lián)系客服