1 回答
TA貢獻1871條經驗 獲得超13個贊
這是一個非常有趣的問題,所以我cd進入了我的 Go 源代碼開始尋找。
時間.睡眠
time.Sleep 定義如下:
// src/time/sleep.go
// Sleep pauses the current goroutine for at least the duration d.
// A negative or zero duration causes Sleep to return immediately.
func Sleep(d Duration)
沒有正文,沒有特定于操作系統的定義time_unix.go?。?!稍微搜索一下,答案是因為time.Sleep實際上是在運行時中定義的:
// src/runtime/time.go
// timeSleep puts the current goroutine to sleep for at least ns nanoseconds.
//go:linkname timeSleep time.Sleep
func timeSleep(ns int64) {
// ...
}
回想起來,這很有意義,因為它必須與 goroutine 調度程序進行交互。它最終調用goparkunlock,它“將 goroutine 置于等待狀態”。time.Sleep創建一個runtime.timer帶有回調函數的回調函數,該函數在計時器到期時調用 - 該回調函數通過調用goready. 有關runtime.timer.
時間.NewTicker
time.NewTicker創建一個*Ticker(并且time.Tick是一個輔助函數,它做同樣的事情但直接返回*Ticker.C,股票代碼的接收通道,而不是*Ticker,所以你可以用它來編寫你的代碼)在運行時有類似的鉤子:股票代碼是一個結構持有一個runtimeTimer和一個通道,在其上發出信號。
runtimeTimer在time包中定義,但它必須與timerin保持同步src/runtime/time.go,因此它實際上是一個runtime.timer. 還記得在time.Sleep,定時器有一個回調函數來喚醒休眠的 goroutine 嗎?在 的情況下*Ticker,定時器的回調函數在股票代碼的通道上發送當前時間。
然后,真正的等待/調度發生在從通道接收時,這與select語句基本相同,除非otherChan在滴答之前發送一些東西,所以讓我們看看阻塞接收時會發生什么。
<- 陳
通道src/runtime/chan.go由hchan結構體在,中實現(現在在 Go 中?。?。通道操作具有匹配功能,接收是通過chanrecv以下方式實現的:
// chanrecv receives on channel c and writes the received data to ep.
// ep may be nil, in which case received data is ignored.
// If block == false and no elements are available, returns (false, false).
// Otherwise, if c is closed, zeros *ep and returns (true, false).
// Otherwise, fills in *ep with an element and returns (true, true).
func chanrecv(t *chantype, c *hchan, ep unsafe.Pointer, block bool) (selected, received bool) {
// ...
}
這部分有很多不同的情況,但在您的示例中,它是來自異步通道的阻塞接收(time.NewTicker創建一個緩沖區為 1 的通道),但無論如何它最終會調用... goparkunlock,再次允許其他 goroutines在這個被卡住等待時繼續。
所以...
在所有情況下,goroutine 最終都會被停住(這并不令人震驚 - 它無法取得進展,因此如果有任何可用的 goroutine,它必須保留其線程可用于不同的 goroutine)??匆谎鄞a似乎表明該通道的開銷比直接time.Sleep. 但是,它允許更強大的模式,例如您示例中的最后一個:goroutine 可以被另一個通道喚醒,以先到者為準。
要回答關于輪詢的其他問題,定時器由一個 goroutine 管理,該協程在隊列中的下一個定時器之前一直處于休眠狀態,因此它僅在知道必須觸發定時器時才工作。當下一個計時器到期時,它會喚醒調用的 goroutine time.Sleep(或在股票代碼的通道上發送值,它會執行回調函數所做的任何事情)。
頻道中沒有輪詢,當在頻道上進行發送時,接收被解鎖,在chansendchan.go 文件中:
// wake up a waiting receiver
sg := c.recvq.dequeue()
if sg != nil {
recvg := sg.g
unlock(&c.lock)
if sg.releasetime != 0 {
sg.releasetime = cputicks()
}
goready(recvg, 3)
} else {
unlock(&c.lock)
}
這是對 Go 源代碼的一個有趣的探索,非常有趣的問題!希望我至少回答了一部分!
- 1 回答
- 0 關注
- 208 瀏覽
添加回答
舉報
