mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2024-11-28 09:09:07 +00:00
1.8 KiB
1.8 KiB
9.8.2. Goroutine調度
OS線程會被操作繫統內核調度。每幾毫秒,一個硬件計時器會中斷處理器,這會調用一個叫作scheduler的內核函數。這個函數會掛起當前執行的線程併保存內存中它的寄存器內容,檢査線程列表併決定下一次哪個線程可以被運行,併從內存中恢複該線程的寄存器信息,然後恢複執行該線程的現場併開始執行線程。因爲操作繫統線程是被內核所調度,所以從一個線程向另一個“移動”需要完整的上下文切換,也就是説,保存一個用戶線程的狀態到內存,恢複另一個線程的到寄存器,然後更新調度器的數據結構。這幾步操作很慢,因爲其局部性很差需要幾次內存訪問,併且會增加運行的cpu週期。
Go的運行時包含了其自己的調度器,這個調度器使用了一些技術手段,比如m:n調度,因爲其會在n個操作繫統線程上多工(調度)m個goroutine。Go調度器的工作和內核的調度是相似的,但是這個調度器隻關註單獨的Go程序中的goroutine(譯註:按程序獨立)。
和操作繫統的線程調度不同的是,Go調度器併不是用一個硬件定時器而是被Go語言"建築"本身進行調度的。例如當一個goroutine調用了time.Sleep或者被channel調用或者mutex操作阻塞時,調度器會使其進入休眠併開始執行另一個goroutine直到時機到了再去喚醒第一個goroutine。因爲因爲這種調度方式不需要進入內核的上下文,所以重新調度一個goroutine比調度一個線程代價要低得多。
練習 9.5: 寫一個有兩個goroutine的程序,兩個goroutine會向兩個無buffer channel反複地發送ping-pong消息。這樣的程序每秒可以支持多少次通信?