區別:線程中資料儲存在內核狀態的記憶體空間;而協程中資料儲存在執行緒提供的用戶狀態記憶體空間。執行緒的任務調度由核心實現,搶佔方式,依賴各種鎖;協程的任務調度由用戶態實現的具體調度器進行。
本教學操作環境:windows10系統、GO 1.11.2、thinkpad t480電腦。
協程
協程,英文名Coroutine。但在 Go 語言中,協程的英文名是:gorutine。它常常被用於進行多任務,即並發作業。沒錯,就是多執行緒作業的那個作業。
雖然在 Go 中,我們不用直接寫線程之類的程式碼來進行並發,但是 Go 的協程卻依賴線程來進行。
下面我們來看看它們的差異。
線程的基礎介紹,這裡請自行上網搜尋文章,因為關於線程的優秀介紹文章已經很多。
協程的特點
這裡先直接列出執行緒的特點,然後再從例子中解析。
多個協程可由一個或多個執行緒管理,協程的調度發生在其所在的執行緒中。
可以被調度,調度策略由應用層程式碼定義,即可被高度自訂實作。
執行效率高。
佔用記憶體少。
上面第1和第2點
我们来看一个例子: func TestGorutine(t *testing.T) { runtime.GOMAXPROCS(1) // 指定最大 P 为 1,从而管理协程最多的线程为 1 个 wg := sync.WaitGroup{} // 控制等待所有协程都执行完再退出程序 wg.Add(2) // 运行一个协程 go func() { fmt.Println(1) fmt.Println(2) fmt.Println(3) wg.Done() }() // 运行第二个协程 go func() { fmt.Println(65) fmt.Println(66) // 设置个睡眠,让该协程执行超时而被挂起,引起超时调度 time.Sleep(time.Second) fmt.Println(67) wg.Done() }() wg.Wait()}
上面的程式碼片段跑了兩個協程,運行後,觀察輸出的順序是交錯的。可能是:
656612367
意味著在執行協程A的過程中,可以隨時中斷,去執協程行B,協程B也可能在執行過程中中斷再去執行協程A。
看起來協程A 和 協程B 的運行像是執行緒的切換,但請注意,這裡的 A 和 B都運行在同一個執行緒裡面。它們的調度不是線程的切換,而是純應用程式的協程調度。
關於上述程式碼中,為什麼要指定下面兩行程式碼?
runtime.GOMAXPROCS(1)time.Sleep(time.Second)
這需要您去看下Go 的協程調度入門基礎,請看我之前的另外一篇調度分析文章:
Go 的協程調度機制
如果不設定runtime.GOMAXPROCS(1),那麼程式將會根據作業系統的CPU 核數而啟動對應數量的P,導致多個M,即執行緒的啟動。那麼我們程式中的協程,就會被分配到不同的執行緒裡面去了。為了演示,故設定數量 1,使得它們都被分配到了同一個線程裡面,存於線程的協程隊列裡面,等待被執行或調度。
協程特點中的第 3和第 4點。
3. 執行效率高。
4. 佔用記憶體少。
因為協程的調度切換不是執行緒切換,而是由程式本身控制,因此,沒有執行緒切換的開銷,和多執行緒比,執行緒數量越多,協程的性能優勢就越明顯。調度發生在應用態而非內核態。
內存的花銷,使用其所在的線程的內存,意味著線程的內存可以供多個協程使用。
其次協程的調度不需要多線程的鎖定機制,因為只有一個線程,也不存在同時寫變數衝突,所以執行效率比多線程高很多。
協程與執行緒的整體比較
#比較的點 | 執行緒 | #協程 |
---|---|---|
資料儲存 | 核心狀態的記憶體空間 | 一般是執行緒提供的使用者狀態記憶體空間 |
切換操作 | 操作最終在核心層完成,應用層需要呼叫核心層提供的syscall 底層函數 | 應用層使用程式碼進行簡單的現場保存和恢復即可 |
任務調度 | 由內核實現,搶佔方式,依賴各種鎖 | 由用戶態的實現的具體調度器進行。例如go 協程的調度器 |
語音支援程度 | 絕大部分程式語言 | 部分語言:Lua,Go,Python … |
實作規格 | 依照現代作業系統規格實作 | 無統一規範。在應用層由開發者實現,高度自訂,例如只支援單執行緒的執行緒。不同的調度策略,等等 |
推薦學習:Golang教學
以上是golang中執行緒和協程的差別是什麼的詳細內容。更多資訊請關注PHP中文網其他相關文章!