2 回答

TA貢獻1827條經(jīng)驗 獲得超4個贊
最好不要將其視為“多線程”。Go 為并發(fā)提供了直接的便利,而不是為線程提供了便利。它碰巧使用線程來實現(xiàn)其并發(fā)性,但這是一個實現(xiàn)細節(jié)。參見 Rob Pike 的演講,Concurrency is not parallelism進行更深入的討論。
您問題的關鍵是默認情況下通道是同步的(如果它們在構建過程中沒有被緩沖)。當一個 goroutine 寫入一個通道時,它會阻塞,直到另一個 goroutine 從該通道讀取。所以當這一行執(zhí)行時:
z := <- d
在此行執(zhí)行之前,它無法繼續(xù):
d <- 0
如果d頻道上沒有可用的價值,fact則永遠不會繼續(xù)。這對你來說很明顯。但反過來也是如此。在從d通道讀取某些內(nèi)容之前,主 goroutine 無法繼續(xù)。通過這種方式,無緩沖通道提供了跨并發(fā) goroutine 的同步點。
同樣,主循環(huán)無法繼續(xù),直到某個值出現(xiàn)在 上c。我發(fā)現(xiàn)使用兩個手指并指向每個 goroutine 中的當前代碼行很有用。前進一根手指,直到進入頻道操作。然后推進另一個,直到它到達一個通道操作。如果您的手指指向同一通道上的讀取和寫入,則您可以繼續(xù)。如果他們不是,那么你就陷入了僵局。
如果你仔細考慮一下,你就會發(fā)現(xiàn)一個問題。該程序泄漏了一個 goroutine。
func fact(n int, c chan int, d chan int) {
k := /* code to compute factorial of n */
z := <- d // (1)
c <- k + z
d <- z + 1 // (2)
}
在 (2) 處,我們嘗試寫入d. 什么將允許它繼續(xù)進行?另一個 goroutine 從d. 請記住,我們啟動了Ngoroutines,并且它們都試圖從d. 只有其中之一會成功。其他人將在 (1) 處阻塞,等待某些東西出現(xiàn)在 上d。當?shù)谝粋€到達(2)時會發(fā)生這種情況。然后那個 goroutine 退出,一個隨機的 goroutine 將繼續(xù)。
但是會有一個最終的 goroutine 永遠無法寫入d并且會泄漏。為了解決這個問題,需要在 final 之前添加以下內(nèi)容Printf:
<-d
這將允許最后一個 goroutine 退出。

TA貢獻1816條經(jīng)驗 獲得超4個贊
如果我們省略主程序中的“d <- 0”行,程序會怎樣,為什么?
有了這行代碼,每個 goroutine 開始go fact(...)
都會等待來自 channel 的東西,在 statement 阻塞z := <- d
。
該fact()
函數(shù)對d
頻道的內(nèi)容沒有實際影響- 它刪除一些內(nèi)容并添加一些內(nèi)容。因此,如果通道中沒有任何內(nèi)容,則不會有任何進展,程序?qū)⑾萑虢┚帧?/p>
從同一通道讀取和寫入的 goroutine 要求死鎖 - 在現(xiàn)實生活中避免!
如果交換fact過程的前兩行,對整個程序的效率有什么影響?
在進行冗長的階乘計算之前,fact()
例程將等到它從d
通道中獲得令牌。
因為d
通道中一次只有一個令牌在播放,這意味著每個 goroutine 只會在收到令牌時進行昂貴的計算,從而有效地將它們序列化。
與最初一樣,昂貴的階乘計算是在等待令牌之前并行完成的。
在實踐中,這不會像您希望的那樣工作,因為 goroutine 不是預先調(diào)度的,僅用于阻塞操作和函數(shù)調(diào)用。
- 2 回答
- 0 關注
- 218 瀏覽
添加回答
舉報