亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

睡眠和選擇的行為

睡眠和選擇的行為

Go
慕絲7291255 2021-11-01 16:21:17
我試圖更多地了解在 Go 中各種阻塞/等待類型的操作期間表面下發生的事情。以下面的例子為例:otherChan = make(chan int)t = time.NewTicker(time.Second)for {    doThings()    // OPTION A: Sleep    time.Sleep(time.Second)    // OPTION B: Blocking ticker    <- t.C    // OPTION C: Select multiple    select {        case <- otherChan:        case <- t.C:    }}從底層來看(系統調用、cpu 調度),這些等待時的區別是什么?我的理解是time.Sleep讓 CPU 可以自由執行其他任務,直到指定的時間過去。阻塞自動收報機<- t.C也做同樣的事情嗎?處理器是否在輪詢通道或是否涉及中斷?選擇中有多個頻道會改變什么嗎?換句話說,假設otherChan從來沒有放入任何東西,這三個選項是否會以相同的方式執行,或者一個比其他的資源密集度更低?
查看完整描述

1 回答

?
慕桂英4014372

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 源代碼的一個有趣的探索,非常有趣的問題!希望我至少回答了一部分!


查看完整回答
反對 回復 2021-11-01
  • 1 回答
  • 0 關注
  • 208 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號