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

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

Golang 阻塞與非阻塞

Golang 阻塞與非阻塞

Go
大話西游666 2021-12-27 15:04:06
我對 Go 如何處理非阻塞 IO 感到有些困惑。API 在我看來大多是同步的,在 Go 上觀看演示時,聽到諸如“和調用塊”之類的評論并不少見從文件或網絡讀取時,Go 是否使用阻塞 IO?或者在 Go Routine 中使用時是否有某種魔法可以重寫代碼?來自 C# 背景,這感覺非常不直觀,在 C# 中,我們await在使用異步 API 時有關鍵字。這清楚地表明 API 可以產生當前線程并稍后在延續中繼續。所以 TLDR; 在 Go 例程中執行 IO 時,Go 會阻塞當前線程,還是會使用延續將其轉換為 C# 之類的 async await 狀態機?
查看完整描述

3 回答

?
忽然笑

TA貢獻1806條經驗 獲得超5個贊

Go 有一個調度程序,可讓您編寫同步代碼,并自行進行上下文切換并在后臺使用異步 IO。因此,如果您正在運行多個 goroutine,它們可能會在單個系統線程上運行,并且當您的代碼從 goroutine 的角度來看被阻塞時,它并不是真正的阻塞。這不是魔術,但是是的,它掩蓋了您的所有這些東西。

調度程序將在需要時以及在真正阻塞的操作期間分配系統線程(例如,我認為文件 IO 正在阻塞,或調用 C 代碼)。但是如果你正在做一些簡單的 http 服務器,你實際上可以使用一些“真實線程”來擁有成千上萬個 goroutine。

您可以在此處閱讀有關 Go 內部工作原理的更多信息:

https://morsmachine.dk/go-scheduler


查看完整回答
反對 回復 2021-12-27
?
慕妹3242003

TA貢獻1824條經驗 獲得超6個贊

您應該先閱讀@Not_a_Golfer 的回答以及他提供的鏈接,以了解 goroutine 的調度方式。我的回答更像是更深入地研究網絡 IO。我假設您了解 Go 如何實現協作式多任務處理。


Go 可以并且確實只使用阻塞調用,因為一切都在 goroutines 中運行,它們不是真正的 OS 線程。它們是綠線。因此,您可以讓它們中的許多都阻塞 IO 調用,并且它們不會像操作系統線程那樣占用您的所有內存和 CPU。


文件 IO 只是系統調用。Not_a_Golfer 已經涵蓋了這一點。Go 將使用真正的操作系統線程來等待系統調用,并在 goroutine 返回時解除阻塞。在這里你可以看到readUnix 的文件實現。


網絡IO不同。運行時使用“網絡輪詢器”來確定哪個 goroutine 應該從 IO 調用中解除阻塞。根據目標操作系統,它將使用可用的異步 API 來等待網絡 IO 事件。調用看起來像阻塞,但內部的一切都是異步完成的。


例如,當您首先調用readTCP 套接字時,goroutine 將嘗試使用 syscall 進行讀取。如果還沒有到達,它將阻塞并等待它恢復。在這里阻塞是指停放,它將 goroutine 放入等待恢復的隊列中。這就是當您使用網絡 IO 時“阻塞”goroutine 將執行交給其他 goroutine 的方式。


func (fd *netFD) Read(p []byte) (n int, err error) {

    if err := fd.readLock(); err != nil {

        return 0, err

    }

    defer fd.readUnlock()

    if err := fd.pd.PrepareRead(); err != nil {

        return 0, err

    }

    for {

        n, err = syscall.Read(fd.sysfd, p)

        if err != nil {

            n = 0

            if err == syscall.EAGAIN {

                if err = fd.pd.WaitRead(); err == nil {

                    continue

                }

            }

        }

        err = fd.eofError(n, err)

        break

    }

    if _, ok := err.(syscall.Errno); ok {

        err = os.NewSyscallError("read", err)

    }

    return

}

https://golang.org/src/net/fd_unix.go?s=#L237


當數據到達時,網絡輪詢器將返回應該恢復的 goroutine。您可以在此處 findrunnable看到搜索可以運行的 goroutine 的函數。它調用netpoll函數,該函數將返回可以恢復的 goroutine。你可以kqueue在netpoll 這里找到實現。


至于 C# 中的異步/等待。異步網絡 IO 還將使用異步 API(Windows 上的 IO 完成端口)。當有東西到達時,操作系統將在線程池的完成端口線程之一上執行回調,這將繼續在當前SynchronizationContext. 從某種意義上說,有一些相似之處(停放/取消停放確實看起來像調用延續,但在低得多的級別上)但是這些模型非常不同,更不用說實現了。默認情況下,Goroutines 不綁定到特定的 OS 線程,它們可以在其中任何一個線程上恢復,這無關緊要。沒有需要處理的 UI 線程。Async/await 專門用于使用以下命令在同一 OS 線程上恢復工作SynchronizationContext. 并且因為沒有綠色線程或單獨的調度程序 async/await 必須將您的函數拆分為多個回調,這些回調SynchronizationContext基本上是一個無限循環,用于檢查應執行的回調隊列。你甚至可以自己實現它,這真的很容易。


查看完整回答
反對 回復 2021-12-27
?
慕的地8271018

TA貢獻1796條經驗 獲得超4個贊

有一些issues,pull request可能會幫助你:)

它可能會解決一些問題,例如

  1. golang什么時候會阻塞IO操作?

  2. 為什么 golang 只使用async ioforsocket而不是normal file

    https://github.com/golang/go/issues/18507 https://github.com/golang/go/commit/c05b06a12d005f50e4776095a60d6bd9c2c91fac https://github.com/golang/go/issues/6222 https://開頭的github .com/golang/go/issues/6817 常規文件上的 Epoll


查看完整回答
反對 回復 2021-12-27
  • 3 回答
  • 0 關注
  • 458 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

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

幫助反饋 APP下載

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

公眾號

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