3 回答

TA貢獻1806條經驗 獲得超5個贊
Go 有一個調度程序,可讓您編寫同步代碼,并自行進行上下文切換并在后臺使用異步 IO。因此,如果您正在運行多個 goroutine,它們可能會在單個系統線程上運行,并且當您的代碼從 goroutine 的角度來看被阻塞時,它并不是真正的阻塞。這不是魔術,但是是的,它掩蓋了您的所有這些東西。
調度程序將在需要時以及在真正阻塞的操作期間分配系統線程(例如,我認為文件 IO 正在阻塞,或調用 C 代碼)。但是如果你正在做一些簡單的 http 服務器,你實際上可以使用一些“真實線程”來擁有成千上萬個 goroutine。
您可以在此處閱讀有關 Go 內部工作原理的更多信息:
https://morsmachine.dk/go-scheduler

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基本上是一個無限循環,用于檢查應執行的回調隊列。你甚至可以自己實現它,這真的很容易。

TA貢獻1796條經驗 獲得超4個贊
有一些issues
,pull request
可能會幫助你:)
它可能會解決一些問題,例如
golang什么時候會阻塞IO操作?
為什么 golang 只使用
async io
forsocket
而不是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
- 3 回答
- 0 關注
- 458 瀏覽
添加回答
舉報