注:該文用於個人學習記錄和知識交流,如有不足,歡迎指點。

一、IO密集型與CPU密集型的定義

對比維度

IO 密集型操作

CPU 密集型操作

定義

操作過程中主要依賴輸入 / 輸出(IO)交互,CPU 大部分時間處於等待 IO 完成的狀態(如等待磁盤讀寫、網絡數據傳輸等)。

操作過程中主要依賴 CPU 進行計算處理,CPU 持續處於高負載狀態,IO 操作極少或耗時極短。

核心特徵

IO 操作頻繁(如讀寫文件、網絡通信);CPU 利用率低;任務執行時間主要消耗在 IO 等待。

計算任務繁重(如複雜運算、邏輯處理);CPU 利用率高(接近 100%);執行時間主要消耗在計算。

C/C++ 典型例子

1. 文件讀寫(fread/fwriteifstream/ofstream

2. 網絡數據收發(socketrecv/send

3. 數據庫交互(通過 API 執行查詢 / 寫入)

1. 大規模數學計算(矩陣乘法、數值模擬)

2. 數據加密 / 解密(AES、RSA 算法實現)

3. 複雜算法處理(千萬級數據排序、圖形渲染計算)

CPU 使用率

較低(大部分時間等待 IO,CPU 空閒)

較高(持續滿負載運行,充分佔用 CPU 資源)

等待時間佔比

高(IO 等待時間佔總執行時間的絕大部分)

低(幾乎無 IO 等待,總執行時間主要為計算時間)

資源瓶頸

受限於 IO 設備性能(如磁盤讀寫速度、網絡帶寬)

受限於 CPU 處理能力(如核心數量、主頻、緩存大小)

二、IO密集型場景的解決方案

在 C/C++ 中處理 IO 密集型操作(如文件讀寫、網絡通信、數據庫交互等)的核心目標是避免CPU阻塞在IO 等待的時間裏提高 CPU 利用率,並高效處理併發 IO 任務。以下是常見的解決方案及適用場景:

1. 非阻塞 IO + IO 多路複用(一般用於單線程)

單線程中、傳統阻塞 IO 會導致進程 / 線程在 IO 操作時掛起(等待數據就緒),浪費 CPU 資源。非阻塞 IO 配合 IO 多路複用可解決這一問題。

(注意:要想不浪費CPU資源,你當然可以設置多個線程,當線程阻塞時,如果線程數大於內核數時,CPU會釋放出去給需要的線程,但是注意線程的數量是有限制的(在Linux中每個線程佔8M空間),一般應用於fd數量很少的時候,在高併發場景中我們不會採用一IO一線程的形式!!!)

1.1 非阻塞 IO

通過fcntlioctl將文件描述符(fd)設置為非阻塞模式,此時調用read/write等 IO 函數時:

  • 若數據未就緒(或緩衝區滿),函數會立即返回錯誤(EAGAIN/EWOULDBLOCK),而非阻塞等待;
  • 進程可繼續執行其他任務,避免閒置。

1.2 IO 多路複用(核心)

用於同時監控多個 IO 流的就緒狀態(可讀 / 可寫 / 異常),避免對每個 IO 流單獨輪詢,大幅提升效率。常見實現:

機制

特點

適用場景

select

支持跨平台,但 fd 數量有限(默認 1024),每次調用需複製 fd 集合,效率低

簡單場景、低併發

poll

突破 fd 數量限制,但仍需輪詢所有 fd,高併發時效率下降

中等併發、跨平台需求

epoll

Linux 專屬,事件驅動(只關注活躍 fd),無 fd 數量限制,效率極高

高併發場景(如服務器)

kqueue

BSD/macOS 專屬,類似 epoll,支持更多事件類型(如文件修改)

BSD/macOS 系統的高併發場景

(我之前寫了一篇IO多路複用的代碼實現的博文,可以去找一找)

1.3 總結:

維度

具體內容

核心原理

1. 非阻塞 IO:通過fcntlioctl將文件描述符(fd)設為非阻塞模式,IO 操作未就緒時立即返回錯誤(EAGAIN/EWOULDBLOCK),不阻塞進程;

2. IO 多路複用:通過內核機制(select/poll/epoll/kqueue 等)同時監控多個 fd 的就緒狀態(可讀 / 可寫 / 異常),僅處理活躍 IO 流。

解決的問題

傳統阻塞 IO 中,進程 / 線程會因等待 IO 而掛起,導致 CPU 資源浪費;單線程無法高效處理大量併發 IO 流的問題。

優點

- 避免進程 / 線程阻塞等待 IO,CPU 利用率高;

- 事件驅動模式,僅處理就緒的 IO 流,減少無效操作,效率高;

- epoll/kqueue 支持無上限監控 fd,適配高併發場景。

缺點

- select:默認 fd 上限 1024(調整意義有限),每次調用需複製 fd 集合,高併發下效率低;

- poll:無 fd 數量限制,但需輪詢所有 fd,高併發時效率隨 fd 數量增加而下降;- 編程複雜,需手動處理非阻塞 IO 邏輯和事件回調,易出現 “回調地獄”。

應用場景

- 高併發網絡服務(如 Web 服務器、網關、即時通訊服務器);

- 需要同時處理大量 IO 流(如 thousands 級以上連接)的場景。

注意事項

- 高併發場景優先選擇 epoll(Linux)或 kqueue(BSD/macOS),避免使用 select/poll;

- 需熟練處理非阻塞 IO 的返回值(如區分EAGAIN與真正的錯誤);

- 事件回調邏輯需簡潔,避免阻塞事件循環。

2. 多線程 / 線程池

IO 密集型任務中,CPU 大部分時間處於空閒(等待 IO),因此可通過多線程並行處理多個 IO 任務,利用 CPU 多核資源。

注意:當線程阻塞時,CPU會釋放,但是線程的數量是有限制的

a. 在Linux中每個線程佔8M空間

b. 過多線程會導致上下文切換開銷激增。

所以線程一般應用於fd數量很少的時候,在高併發場景中我們不會採用一IO一線程的形式!!!

2.1 核心思路

  • 每個線程處理一個獨立的 IO 流(如一個網絡連接、一個文件);
  • 當線程阻塞在 IO 時,其他線程可繼續運行,避免 CPU 閒置。

2.2 線程池優化

頻繁創建 / 銷燬線程會帶來開銷,線程池通過預先創建固定數量的線程,循環處理任務隊列中的 IO 任務,減少開銷:

2.3 總結:

維度

具體內容

核心原理

1. 多線程:每個線程獨立處理一個 IO 流(如一個網絡連接、一個文件),當線程因 IO 阻塞時,CPU 自動調度其他就緒線程運行;

2. 線程池:預先創建固定數量的線程,循環從任務隊列中獲取 IO 任務並處理,減少線程創建 / 銷燬的開銷。

解決的問題

單線程串行處理多個 IO 流時效率低下的問題,通過多核 CPU 並行處理 IO 任務,提高整體吞吐量。

優點

- 利用多核 CPU 並行處理,提升 IO 任務吞吐量;

- 基於同步阻塞 IO 模型,編程邏輯簡單,開發和維護成本低;

- 線程池避免頻繁創建 / 銷燬線程的開銷,適合任務頻繁的場景。

缺點

- 線程棧內存佔用大(Linux 默認 8MB,可調整但仍有下限),IO 流過多時易導致內存壓力;

- 線程上下文切換為內核級操作,開銷高(約幾微秒),線程數量過多會抵消並行收益;

- 線程間共享數據時易出現鎖競爭,導致核心阻塞。

應用場景

- IO 流數量較少的場景(如幾十到幾百個連接 / 文件);

- 任務邏輯簡單、IO 阻塞時間較長的場景(如少量文件批量讀寫、低併發數據庫交互)。

注意事項

- 線程數量建議控制在 “CPU 核心數 ×2” 以內,避免過多線程導致切換開銷激增;

- 可通過pthread_attr_setstacksize調整線程棧大小,減少內存佔用;

- 減少線程間共享數據,優先使用無鎖結構或線程本地存儲,避免鎖競爭。

3. 協程(輕量併發)

(關於協程的實現我會在後續博文詳細介紹)

協程是用户態的 “輕量級線程”,在 IO 操作時可主動掛起,讓出 CPU 給其他協程,避免線程上下文切換的開銷,適合處理海量併發 IO(如十萬級網絡連接)。

3.1 C++ 中的協程

  • C++20 引入std::coroutine,但需配合編譯器支持(如 GCC 10+、Clang 15+);
  • 第三方庫:Boost.Coroutine、libco(騰訊)等,封裝了協程切換邏輯。

3.2 優勢

  • 協程切換成本遠低於線程(無需內核參與);
  • 單線程可運行上萬協程,高效處理大量 IO 任務。
  • 用同步的代碼風格寫異步邏輯

(注意:協程的切換通常搭配epoll實現)

3.3 總結:

維度

具體內容

核心原理

協程是用户態的輕量級併發單元,擁有獨立的棧空間但由用户態調度;

當執行 IO 操作時,協程主動掛起(用户態切換,無需內核參與),讓出 CPU 給其他協程;

依賴底層 IO 多路複用(如 epoll)監控 IO 就緒狀態,當 IO 就緒時喚醒對應的協程。

解決的問題

多線程在高併發場景下內存佔用大(線程棧)、上下文切換開銷高的問題,支持海量 IO 流(十萬級以上)的高效併發處理。

優點

- 協程切換為用户態操作,開銷極低(約幾十納秒,僅為線程切換的 1/100~1/1000);

- 單線程可承載上萬甚至幾十萬協程,支持海量併發 IO;

- 支持用同步代碼風格編寫異步邏輯,避免 “回調地獄”,可讀性和可維護性高。

缺點

- 需依賴底層 IO 多路複用(如 epoll)實現 IO 就緒通知,否則協程掛起後無法被喚醒;

- 協作式調度,若某協程執行 CPU 密集型操作(長時間不掛起),會阻塞同一線程內的其他協程;

- 依賴第三方庫(如 Boost.Coroutine、libco)或 C++20 標準協程(編譯器支持有限,使用門檻高)。

應用場景

- 十萬級以上海量併發 IO 場景(如高併發客户端、分佈式爬蟲、微服務網關);

- 每個任務包含多次 IO 等待的場景(如多次數據庫查詢、多步網絡請求)。

注意事項

- 必須與 IO 多路複用(如 epoll)配合使用,否則無法發揮高效併發能力;

- 避免在協程中執行長時間 CPU 密集型操作,需主動掛起讓出 CPU;

- 根據開發環境選擇合適的協程實現(優先成熟第三方庫,C++20 標準協程需確認編譯器支持)。

三、CPU密集型的解決方案

在 C/C++ 中,CPU 密集型操作(如數值計算、圖像處理、加密解密、複雜算法推理等)的核心瓶頸是CPU 計算能力,解決方案的核心目標是最大化利用 CPU 資源(如多核並行、指令級優化、減少計算冗餘等)。以下是具體方案及適用場景:

1.多線程並行(利用多核 CPU)

現代 CPU 普遍為多核架構,單線程只能利用一個核心,多線程可將任務拆分到多個核心並行執行,直接提升計算吞吐量。

1.1 核心思路

  • 任務拆分:將 CPU 密集型任務按邏輯拆分為獨立子任務(如分塊處理數組、分片計算矩陣),每個子任務由一個線程執行;
  • 線程數控制:線程數通常設置為CPU 核心數(或核心數 ±1),避免過多線程導致上下文切換開銷抵消並行收益(CPU 密集型任務中,線程切換成本極高:當線程數大於內核數時,如果每個線程都需要CPU,那麼CPU會進行輪詢。輪詢的時間成本高於CPU實際計算的時間)。

2.2 實現方式

  • 原生線程(pthread/C++11 thread):手動創建線程並分配任務,適合簡單場景。
  • 線程池:避免頻繁創建 / 銷燬線程的開銷(尤其任務短而多的場景),通過預先創建固定數量的線程循環處理任務隊列。適合任務粒度小、動態生成的場景(如批量圖像處理)。

2.3 注意事項

  • 避免鎖競爭:CPU 密集型任務中,線程間若頻繁競爭鎖(如共享數據寫入),會導致大量核心阻塞,嚴重降低性能。建議:
  • 儘量使用無鎖數據結構(如std::atomic)或線程本地存儲(TLS)
  • 拆分任務時讓數據無重疊(每個線程處理獨立數據塊),減少共享。
  • 負載均衡:確保子任務計算量均勻,避免 “某線程早結束,其他線程仍在忙碌” 的情況(可通過動態任務分配優化,如 TBB 的parallel_for)。

2.4 總結:

維度

具體內容

核心原理

基於多核 CPU 架構,將 CPU 密集型任務按邏輯拆分為獨立子任務(如數據分塊、計算分片),通過多線程將子任務分配到不同 CPU 核心並行執行;

線程由操作系統內核調度,當線程數與核心數匹配時,可最大化利用多核計算能力。

解決的問題

單線程只能利用單個 CPU 核心,無法發揮多核架構的計算潛力,導致 CPU 密集型任務(如數值計算、圖像處理)執行效率低下的問題。

優點

- 高效利用多核資源:通過並行計算直接提升吞吐量,理論上 N 核 CPU 可實現近 N 倍加速(忽略任務拆分開銷);

- 線程通信成本低:線程共享進程地址空間,可直接訪問共享內存(需同步機制),數據交互效率高於進程間通信;

- 線程池優化:預先創建線程減少頻繁創建 / 銷燬的開銷,適合短任務、動態生成任務的場景。

缺點

- 上下文切換開銷高:當線程數超過 CPU 核心數時,內核需頻繁切換線程(保存 / 恢復寄存器、棧等狀態),每次切換約幾微秒,會抵消並行收益;

- 同步成本風險:線程共享數據時需鎖或原子操作,頻繁鎖競爭會導致核心阻塞(“串行化”),嚴重降低性能;

- 資源限制:每個線程默認棧空間較大(Linux 約 8MB),過多線程會佔用大量內存。

應用場景

- 可拆分為獨立子任務的 CPU 密集型操作:如大數組運算、矩陣乘法、圖像分塊處理、批量加密解密;

- 任務粒度適中且可動態生成的場景:如實時視頻幀處理、批量數據清洗與轉換。

注意事項

- 線程數控制:通常設為 “CPU 核心數” 或 “核心數 ±1”,避免線程數超過核心數導致切換開銷激增;- 減少共享與鎖競爭:優先採用 “數據無重疊拆分”(每個線程處理獨立數據塊),必要時用std::atomic(原子操作)或線程本地存儲(TLS)替代鎖;

- 負載均衡:確保子任務計算量均勻(如通過動態任務分配,如 TBB 的parallel_for),避免 “部分線程閒置、部分線程忙碌”。

2. 多進程並行(隔離性與多核利用)

多進程與多線程的核心區別是內存隔離(進程間不共享地址空間),適合以下場景:

  • 任務穩定性要求高(單個進程崩潰不影響其他進程);
  • 利用操作系統的 CPU 調度(避免線程庫調度的侷限性)。

2.1 實現方式

  • 通過fork(Linux)或CreateProcess(Windows)創建子進程,通過進程間通信(IPC) 傳遞數據(如管道、共享內存、消息隊列)。
  • 共享內存(如shmget/mmap)是 CPU 密集型任務的優選 IPC 方式(避免數據拷貝開銷)。

2.2 優劣

  • 優勢:隔離性好,適合不穩定任務;可利用操作系統調度均衡負載。
  • 劣勢:IPC 開銷高於線程間共享內存;進程創建 / 銷燬成本高於線程。

2.3 總結:

維度

具體內容

核心原理

通過創建多個獨立進程(如 Linux 的fork、Windows 的CreateProcess),將 CPU 密集型任務拆分到不同進程,利用多核 CPU 並行執行;進程間內存隔離(不共享地址空間),需通過進程間通信(IPC)傳遞數據或同步狀態。

解決的問題

- 單線程進程無法利用多核 CPU,導致 CPU 密集型任務效率低下;

- 多線程方案中 “線程崩潰可能導致整個進程崩潰” 的穩定性風險,以及線程庫調度在某些場景下不如系統級進程調度靈活的侷限性

優點

- 隔離性強:進程擁有獨立地址空間,單個進程崩潰(如內存越界、斷言失敗)不會影響其他進程,適合穩定性要求高的任務(如異構計算節點、第三方不可靠模塊);

- 調度更靈活:可利用操作系統原生調度機制(避免線程庫調度的侷限性),在複雜負載下可能獲得更均衡的核心利用;

- 資源隔離:進程間內存、文件描述符等資源獨立,可避免線程間的資源競爭(如堆內存碎片影響)。

缺點

- IPC 開銷高:進程間數據交互需通過管道、消息隊列、共享內存等方式,其中共享內存雖高效但需手動管理同步,其他方式(如管道)存在數據拷貝開銷,效率低於線程間直接共享;

- 管理成本高:進程創建 / 銷燬比線程更耗時(涉及地址空間初始化、頁表創建等),不適合短任務頻繁生成的場景;

- 內存佔用高:每個進程需獨立加載代碼、數據,重複佔用內存(如相同庫的代碼段在多進程中可能被共享,但堆 / 棧獨立)。

應用場景

- 穩定性優先的 CPU 密集型任務:如分佈式計算節點(單個節點崩潰不影響集羣)、集成第三方不穩定算法(如實驗性模型推理);

- 需資源隔離的場景:如多用户計算任務(避免用户間資源干擾)、對內存泄漏敏感的長期運行任務;

- 利用系統級調度優勢的場景:如跨 NUMA 節點的大規模計算(進程更易綁定到特定 NUMA 節點)。

注意事項

- 優先選擇高效 IPC:CPU 密集型任務中,推薦用共享內存(如mmapshmget)傳遞數據,減少拷貝開銷,配合信號量或原子操作實現同步;

- 進程數控制:與線程類似,進程數建議不超過 CPU 核心數(避免調度開銷),或根據 NUMA 節點數合理分配;

- 避免重複初始化:通過共享庫或預加載數據減少多進程的重複初始化開銷(如提前加載模型權重到共享內存)。

四、線程的狀態

狀態名稱

定義(核心特徵)

觸發條件(進入該狀態的原因)

轉換方向(可進入的其他狀態)

新建(New)

線程已被創建(如通過pthread_createstd::thread),但尚未啓動(未調用start或未執行)。

調用線程創建函數(如pthread_create)後,線程對象初始化完成但未開始執行。

調用啓動接口(如std::thread::joinable狀態下開始執行)後,進入就緒狀態

就緒(Ready)

線程已啓動,具備執行條件(CPU 資源除外),等待操作系統調度分配 CPU 時間片。

線程新建後啓動;或阻塞狀態結束(如 IO 完成、鎖釋放);

或被搶佔後重新進入就緒隊列。

被操作系統調度獲得 CPU 時間片後,進入運行狀態

運行(Running)

線程正在 CPU 核心上執行指令,佔用 CPU 資源。

從就緒狀態被調度器選中,獲得 CPU 時間片。

時間片耗盡或被高優先級線程搶佔,回到就緒狀態;或因等待資源(如鎖、IO),進入阻塞狀態;或執行完畢,進入終止狀態

阻塞(Blocked)

線程暫時停止執行,放棄 CPU 資源,等待特定條件滿足(如 IO 完成、鎖釋放、信號通知)。

1. 等待互斥鎖(如pthread_mutex_lock未獲取到鎖);

2. 執行 IO 操作(如read等待數據);

3. 調用阻塞式同步接口(如pthread_cond_wait等待條件變量)。

等待的條件滿足(如鎖釋放、IO 完成)後,進入就緒狀態

等待(Waiting)

阻塞狀態的細分,線程無超時等待某個事件(需被其他線程顯式喚醒)。(部分模型中獨立為狀態)

調用無超時等待接口(如pthread_cond_waitstd::condition_variable::wait)。

被其他線程喚醒(如pthread_cond_signal)後,進入就緒狀態

超時等待(Timed Waiting)

阻塞狀態的細分,線程有超時等待某個事件(超時後自動喚醒)。(部分模型中獨立為狀態)

調用帶超時的等待接口(如pthread_cond_timedwaitsleepstd::this_thread::sleep_for)。

超時時間到達或被提前喚醒後,進入就緒狀態

終止(Terminated)

線程執行完畢(如函數返回)或被強制終止(如pthread_cancel),生命週期結束。

1. 線程函數正常返回;

2. 調用終止接口(如pthread_exit);

3. 被其他線程取消(如pthread_cancel)。

無後續狀態轉換,線程對象可能保留(如std::threadjoinable狀態需joindetach釋放資源)。

説明:

  1. 狀態劃分的靈活性:部分系統(如 Linux)將 “就緒” 和 “運行” 合併為 “可運行(R)” 狀態(內核視角下,兩者均為就緒隊列中的任務,僅運行狀態是當前佔用 CPU 的任務)。
  2. 阻塞狀態的細分:“等待”“超時等待” 是阻塞狀態的特殊場景,核心都是線程放棄 CPU 等待資源,部分模型中會將其獨立為狀態以更清晰描述等待類型。
  3. 核心邏輯:線程狀態轉換的核心是”是否需要CPU“ “是否佔用 CPU” 和 “是否等待資源”,從新建到終止的生命週期圍繞這兩點展開。

總的來説:線程的狀態影響着操作系統是否分配CPU給它!!!

五、CPU調度情況

1.線程數不同時

維度

線程數 ≤ CPU 內核數

線程數 > CPU 內核數

CPU 利用率

高,每個線程可在獨立內核上並行執行,無閒置核心

理論上仍為 100%,但實際因上下文切換開銷,有效計算時間被佔用,實際有效利用率下降

上下文切換開銷

低,線程間切換主要為CPU的主動讓出(如阻塞、線程結束等等)

高,內核需頻繁切換線程(保存 / 恢復線程狀態),切換開銷佔比顯著增加

任務吞吐量

隨線程數增加線性提升(接近內核數時達到峯值)

吞吐量先增後減,線程數超過內核數後,因切換開銷抵消並行收益,吞吐量開始下降

資源消耗(內存等)

低,每個線程棧空間(如 Linux 默認 8MB)總消耗可控

高,過多線程的棧空間、內核調度數據結構會佔用大量內存,可能引發內存壓力

適用場景

CPU 密集型任務(如數值計算、圖像處理),需最大化核心利用率

IO 密集型任務(如網絡通信、文件讀寫),線程因 IO 阻塞時可讓出 CPU 給其他線程

性能表現

響應時間穩定,無額外調度延遲

響應時間波動大,調度延遲增加(線程需等待 CPU 時間片)

2. 搶佔式調度

項目

具體內容

核心結論

當線程數 > CPU 內核數時,操作系統對線程的調度屬於搶佔式調度

原因分析

現代操作系統(如 Linux、Windows)的線程調度機制默認是搶佔式的:

當線程數超過 CPU 內核數時,多個線程需共享有限的 CPU 核心,操作系統會為每個線程分配時間片(一段可執行的時間);時間片耗盡後,操作系統會主動中斷當前線程(搶佔其 CPU 使用權),並切換到下一個線程執行,以保證所有線程公平獲得 CPU 資源,避免某線程長期佔用 CPU 導致其他線程 “飢餓”。

具體表現

- 每個線程會被分配時間片(如 Linux 默認時間片約為幾毫秒),時間片內線程可獨佔一個 CPU 核心執行;

- 時間片耗盡後,操作系統會強制切換到下一個線程,即使當前線程的計算任務未完成。

補充説明

- 搶佔式調度是內核級的,由操作系統內核主動控制,與線程是否 “需要 CPU” 無關,只要線程處於就緒狀態,就可能被調度器搶佔或分配 CPU 時間;

- 若線程因 IO 操作阻塞(如網絡讀寫),會主動讓出 CPU(屬於 “協作式讓出”),但整體調度機制仍為搶佔式(其他就緒線程會被搶佔式調度執行)。

3. 非搶佔式調度

項目

具體內容

核心結論

當線程數 ≤ CPU 內核數時,操作系統對線程的調度以 “非搶佔為主,必要時搶佔”(多數場景下線程可在分配的核心上持續執行,減少強制切換)。

原因分析

現代操作系統雖默認採用搶佔式調度機制,但此時線程數不超過核心數,每個線程可分配到獨立的 CPU 核心,無需共享核心資源:

- 若線程均為 CPU 密集型且優先級相同,操作系統通常不會主動搶佔,允許線程在核心上持續執行(減少切換開銷);

- 僅在特殊場景(如高優先級線程就緒、當前線程觸發中斷或阻塞)時,才會觸發搶佔式切換。

具體表現

- 線程可在分配的 CPU 核心上 “長時運行”,時間片限制較寬鬆(甚至不嚴格耗盡時間片),上下文切換極少;

- 若某線程因 IO 操作阻塞或主動讓出 CPU(如yield),操作系統會調度其他就緒線程到空閒核心,此時切換屬於 “協作式讓出” 而非強制搶佔;

- 高優先級線程就緒時,仍會搶佔低優先級線程的核心(保證實時性)。

補充説明

- 此場景下 CPU 利用率接近 100%(無閒置核心),且上下文切換開銷極低,適合 CPU 密集型任務(如數值計算、圖像處理);

- “非搶佔為主” 不代表完全無搶佔,操作系統仍會通過搶佔機制保證高優先級任務的響應性(如實時線程、中斷處理);

- 若線程數小於核心數,空閒核心會處於 “idle” 狀態,操作系統可能將線程遷移到空閒核心以平衡負載(非搶佔,屬於主動調度優化)。

注意:本質上還是採取搶佔式(還是有時間片的設置),只是每個線程可以得到單獨的CPU核心,時間片很長罷了!!!表現為操作系統不頻繁調度CPU資源。

六、CPU調度與線程切換的關係

線程切換是操作系統進行 CPU 調度時的核心操作之一,二者緊密關聯但不完全等同。

具體關係拆解:

  • CPU 調度(Scheduling) 是一個決策過程:操作系統的調度器根據策略(如優先級、時間片、公平性等),從就緒隊列中選擇下一個要在 CPU 上執行的線程。例如:當線程時間片耗盡、被高優先級線程搶佔、或主動阻塞時,調度器需要決定 “接下來讓哪個線程運行”。
  • 線程切換(Context Switch) 是執行過程:當調度器選定下一個線程後,操作系統會實際執行 “從當前線程切換到目標線程” 的動作 —— 保存當前線程的狀態(寄存器、程序計數器、棧指針等),加載目標線程的狀態,讓其在 CPU 上繼續執行。

總結:

線程切換是 CPU 調度過程中 “執行決策” 的關鍵步驟,沒有線程切換,調度器的決策無法落地;而 CPU 調度則包含了 “選擇線程” 和 “執行切換” 的完整邏輯。因此,線程切換可以理解為操作系統 CPU 調度機制中最核心的操作環節。

七、要點(核心總結)

1. IO密集型、CPU密集型

IO密集型表現為頻繁IO等待(非本線程處理數據):在高性能網絡中、我們需要做到不阻塞,避免浪費CPU性能(可以採用非阻塞IO加epoll、多線程、協程)

CPU密集型表現為頻繁數據處理(本線程處理數據):我們需要做到充分利用CPU的核數、因為一線程只能對應一核,我們可以採用多線程(注意線程數小於等於核數,避免線程切換開銷)、多進程的處理方案。

2. CPU的調度情況

線程的狀態和個數與系統對CPU資源的調度息息相關:
我們必須理解線程數的設置如何影響CPU調度的頻率、同時理解操作系統是如何根據線程狀態來進行CPU資源的分配的!!!

3. 以上的知識有助於後面理解協程

大家一定要理解上面這些,因為內核的操作開銷都是比較大的(我們必須想辦法降低開銷)。

線程的數量的限制都是為了性能:線程數量大了、佔用內存大、同時切換開銷大(延遲高、不適合高併發IO場景)、切換規則也不容易更改。

瞭解了這些限制你才會明白為什麼需要在用户態又搞個協程。

協程又稱為輕量級線程,它也有輕量棧、狀態、調度器(類似一個管理CPU分配的內核)。它相比於線程(內核管理,開銷大、不靈活)、更加靈活輕量、CPU的分配完全由用户自定義。

(我會在後續博文詳細介紹協程給大家,包括協程的優點、理念、以及協程庫的實現和妙用!!!!)