1.棧和堆的區別
可以視為臨時變量和手動申請內存空間的變量的區別
func example() {
a := 10 // 分配在棧上
b := new(int) // b 是在堆上分配的一個 int 指針
*b = 20
}
1.棧的內存分配是連續的,堆分配需要查找空閑塊
2.棧是編譯器自動分配和釋放,堆需要自己申請空間
3.棧的內存小,堆的內存大
4.棧是后進先出,堆是任意順序
2.進程和線程的區別
進程
有自己獨立的物理內存空間,掛靠操作系統分配資源,使用的是堆和棧。是操作系統分配系統資源的最小單位。
┌───────────────┐ ← 高地址
│ 棧(Stack)│ ← 每個線程一個,向下增長
├───────────────┤
│ 堆(Heap)│ ← 動態申請內存區,向上增長
├───────────────┤
│ BSS段 │ ← 未初始化的全局變量
├───────────────┤
│ 數據段(Data)│ ← 初始化的全局/靜態變量
├───────────────┤
│ 代碼段(Text)│ ← 程序指令(只讀)
└───────────────┘ ← 低地址
查看對應部分所占內存的命令:
堆/棧: cat /proc/[pid]/maps 后面對應有[heap]/[stack] 標志的代表的就是對應的堆/棧
bss/data/text: readelf-S /bin/ls 路徑可以根據自己的來,對應字段下標志的大小就是所占的內存
線程
輕量級進程,是操作系統調度(CPU調度)執行的基本單位,使用的是堆和棧。
棧區對于線程來說不共享,但是堆是共享的。
線程在創建的時候就會分配一塊單獨的棧空間(1MB-8MB)
就像每個函數里面的臨時變量是不共享的,但是申請了空間的變量是可以共享的。
區別
- 進程之間不共享內存,線程之間會共享內存
- 進程切換操作成本高,線程較低
- 進程通信方式是管道、消息隊列,線程是內存共享、鎖
- 進程之間可并發,一個進程中的多個線程也可并發
- 進程擁有系統資源(靜態資源:地址空間、文件系統狀態、信號處理handler)
線程不擁有系統資源(動態資源:運行棧、調度的控制信息、待處理的信號集) - 進程有自己獨立的地址空間,崩潰后不會對其他進程產生影響。
線程是一個進程中的不同執行路徑,一個線程沒有自己的地址空間,死掉(死鎖)之后該進程中的所有線程都會死掉。多進程的程序比多線程健壯,但是效率差。
親緣性
進程/線程只在某個cpu上運行,不允許調度到其他核心
| 場景 | 解釋 |
|---|---|
| 提升緩存命中率 | 綁定到某個核心可以利用該核心的緩存數據(L1/L2),避免頻繁調度導致緩存失效 |
| 減少上下文切換開銷 | 如果任務老被調到不同核心,會產生額外的CPU上下文切換開銷 |
| 控制資源分配 | 在多核機器上,進行線程分離,比如一個核負責網絡線程,另一個核負責 IO,可以提升吞吐量 |
| 性能調優 | 在性能測試、實時系統(如游戲引擎、工業控制)中,有時會強制綁定特定線程運行在特定 CPU 上 |
taskset -c 0 ./your_program # 將程序綁定在 CPU 0 上運行
taskset -p <PID>//查看當前進程親緣性
協程
協程通過程序切換,消耗資源少,通過棧實現。goroutine就是協程(輕量級線程,依賴于線程運行但并不是線程,共享一個線程的棧堆等所有空間),程序的執行路徑是線程,所有執行路徑的總體是進程
協程是在用戶態執行,開銷很小,速度很快,性能得到了極大的提升。
協程是可中斷的,協程可以存儲中斷前的狀態,并且在中斷后恢復時繼續使用之前的狀態(協程本身不會自動存儲和恢復中斷前的狀態,但可以手動保存當前狀態并在恢復時繼續運行)。
保存的內容一般是:
1.當前處理的任務 ID 或數據
2.已完成進度(比如處理了 100 條數據中的前 20 條)
3.關鍵變量值(緩存、臨時數據)
4.狀態機位置(在哪一步暫停)
手動保存可以通過redis/mysql/文件存儲state,context.Context + 狀態存儲,來恢復
協程較線程的優勢:
1.沒有線程切換,少了很多io開銷,由程序本身控制,較多線程性能提升較大,雖然協程的調度由用戶自己控制(例如 Go 語言的 Goroutine 由 Go runtime 調度),它的調度并不像線程那樣由操作系統內核管理,因此創建和銷毀的開銷極低。而且協程能夠在同一個線程上并發執行,大大減少了上下文切換的成本,從而提高了效率。
2.不需要鎖,一個線程沒有同時寫的沖突,共享資源不加鎖,只用判斷狀態
浙公網安備 33010602011771號