進程、線程和協程區別
1. 進程
進程是系統資源分配的最小單位, 系統由一個個進程(程序)組成一般情況下,包括文本區域(text region)、數據區域(data region)和堆棧(stack region)。
- 文本區域存儲處理器執行的代碼
- 數據區域存儲變量和進程執行期間使用的動態分配的內存;
- 堆棧區域存儲着活動過程調用的指令和本地變量。
因此進程的創建和銷毀都是相對於系統資源,所以是一種比較昂貴的操作。 進程有三個狀態:
- 等待態:等待某個事件的完成;
- 就緒態:等待系統分配處理器以便運行;
- 運行態:占有處理器正在運行。
進程是搶占式的爭奪CPU運行自身,而CPU單核的情況下同一時間只能執行一個進程的代碼,但是多進程的實現則是通過CPU飛快的切換不同進程,因此使得看上去就像是多個進程在同時進行.
通信問題: 由於進程間是隔離的,各自擁有自己的內存內存資源, 因此相對於線程比較安全, 所以不同進程之間的數據只能通過 IPC(Inter-Process Communication) 進行通信共享.
2. 線程
進程與線程其實最核心的是隔離與並行。
- 線程屬於進程
- 線程共享進程的內存地址空間
- 線程幾乎不占有系統資源
通信問題: 進程相當於一個容器,而線程而是運行在容器里面的,因此對於容器內的東西,線程是共同享有的,因此線程間的通信可以直接通過全局變量進行通信,但是由此帶來的例如多個線程讀寫同一個地址變量的時候則將帶來不可預期的后果,因此這時候引入了各種鎖的作用,例如互斥鎖等。
同時多線程是不安全的,當一個線程崩潰了,會導致整個進程也崩潰了,即其他線程也掛了,但多進程而不會,一個進程掛了,另一個進程依然照樣運行。
線程能更好的使用cpu資源,即使是單核也能加大進程搶到cpu的機會,並且一個進程中的多個線程可以被多個處理器並行執行。
- 進程是系統分配資源的最小單位
- 線程是CPU調度的最小單位
- 由於默認進程內只有一個線程,所以多核CPU處理多進程就像是一個進程一個核心
3. 進程與線程對比
3.1 線程和進程的上下文切換
進程切換分3步:
- 切換頁目錄以使用新的地址空間
- 切換內核棧
- 切換硬件上下文
而線程切換只需要第2、3步,因此進程的切換代價比較大
3.2 其他方面
- 地址空間:線程是進程內的一個執行單元,進程內至少有一個線程,它們共享進程的地址空間,而進程有自己獨立的地址空間
- 資源擁有:進程是資源分配和擁有的單位,同一個進程內的線程共享進程的資源
- 線程是處理器調度的基本單位,但進程不是
- 二者均可並發執行
- 每個獨立的線程有一個程序運行的入口、順序執行序列和程序的出口,但是線程不能夠獨立執行,必須依存在應用程序中,由應用程序提供多個線程執行控制
4. 協程
對於進程、線程,都是有內核進行調度,有CPU時間片的概念,進行搶占式調度(有多種調度算法)。
對於協程(用戶級線程),這是對內核透明的,也就是系統並不知道有協程的存在,是完全由用戶的程序自己調度的,因為是由用戶程序自己控制,那么就很難像搶占式調度那樣做到強制的CPU控制權切換到其他進程/線程,通常只能進行協作式調度,需要協程自己主動把控制權轉讓出去之后,其他協程才能被執行到。
- 協程是屬於線程的。協程程序是在線程里面跑的,因此協程又稱微線程和纖程等
- 協沒有線程的上下文切換消耗。協程的調度切換是用戶(程序員)手動切換的,因此更加靈活,因此又叫用戶空間線程.
- 原子操作性。由於協程是用戶調度的,所以不會出現執行一半的代碼片段被強制中斷了,因此無需原子操作鎖。
5. 線程與協程對比
- 一個線程可以多個協程,即一個內核線程對應多個用戶協程(用戶進程)。
- 進程、線程,都是有內核進行調度,有CPU時間片的概念,進行搶占式調度(有多種調度算法)
- 協程的調度與內核無關,完全有程序進行控制。只能進行非搶占式調度。
- 協程能保留上一次調用時的狀態,每次過程重入時,就相當於進入上一次調用的狀態
- 極高的執行效率:因為子程序切換不是線程切換,而是由程序自身控制,因此,沒有線程切換的開銷,和多線程比,線程數量越多,協程的性能優勢就越明顯
比較的點 | 線程 | 協程 |
---|---|---|
數據存儲 | 內核態的內存空間 | 一般是線程提供的用戶態內存空間 |
切換操作 | 操作最終在內核層完成,應用層需要調用內核層提供的 syscall 底層函數 | 應用層使用代碼進行簡單的現場保存和恢復即可 |
任務調度 | 由內核實現,搶占方式,依賴各種鎖 | 由用戶態的實現的具體調度器進行。例如 go 協程的調度器 |
語音支持程度 | 絕大部分編程語言 | 部分語言:Lua,Go,Python ... |
實現規范 | 按照現代操作系統規范實現 | 無統一規范。在應用層由開發者實現,高度自定義,比如只支持單線程的線程。不同的調度策略,等等 |
5.1. goroutine
本質上,goroutine 就是協程。 不同的是,Golang 在 runtime、系統調用等多方面對 goroutine 調度進行了封裝和處理,當遇到長時間執行或者進行系統調用時,會主動把當前 goroutine 的CPU (P) 轉讓出去,讓其他 goroutine 能被調度並執行,也就是 Golang 從語言層面支持了協程。
- 內存消耗方面
每個 goroutine (協程) 默認占用內存遠比 Java 、C 的線程少。 goroutine: 2KB 線程: 8MB
- 線程/goroutine 切換(調度)開銷方面
線程/goroutine 切換開銷方面,goroutine 遠比線程小 線程: 涉及模式切換(從用戶態切換到內核態)、16個寄存器、PC、SP...等寄存器的刷新等。 goroutine: 只有三個寄存器的值修改 - PC / SP / DX.
6. 同步(Sync)和異步(Async)
6.1 同步:
所謂同步,就是發出一個功能調用時,在沒有得到結果之前,該調用就不返回或繼續執行后續操作。
簡單來說,同步就是必須一件一件事做,等前一件做完了才能做下一件事。
例如:B/S模式中的表單提交,具體過程是:客戶端提交請求->等待服務器處理->處理完畢返回,在這個過程中客戶端(瀏覽器)不能做其他事。
6.2 異步:
異步與同步相對,當一個異步過程調用發出后,調用者在沒有得到結果之前,就可以繼續執行后續操作。當這個調用完成后,一般通過狀態、通知和回調來通知調用者。對於異步調用,調用的返回並不受調用者控制。
對於通知調用者的三種方式,具體如下:
狀態
即監聽被調用者的狀態(輪詢),調用者需要每隔一定時間檢查一次,效率會很低。
通知
當被調用者執行完成后,發出通知告知調用者,無需消耗太多性能。
回調
與通知類似,當被調用者執行完成后,會調用調用者提供的回調函數。
例如:B/S模式中的ajax請求,具體過程是:客戶端發出ajax請求->服務端處理->處理完畢執行客戶端回調,在客戶端(瀏覽器)發出請求后,仍然可以做其他的事。
同步和異步的區別:
總結來說,同步和異步的區別:請求發出后,是否需要等待結果,才能繼續執行其他操作。
問題
1. 協程之間是同步還是異步,對於同一個線程而言,里面的不同的協程是同步還是異步
同步還是異步是要根據具體情況來分析
參考鏈接:
https://segmentfault.com/q/1010000004878639
https://blog.csdn.net/daaikuaichuan/article/details/82951084?utm_source=distribute.pc_relevant.none-task
https://juejin.im/post/5b0014b7518825426e023666
https://github.com/iostalks/Blog/issues/1
https://blog.csdn.net/u013007900/article/details/89016375
https://www.jianshu.com/p/dd4a480a1410
http://www.sizeofvoid.net/goroutine-under-the-hood/
https://blog.csdn.net/qq_41853758/article/details/83514635