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

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

Goroutines 節流示例

Goroutines 節流示例

Go
慕慕森 2022-06-01 17:50:13
我正在通過 Udemy 課程學習基本的圍棋。在 goroutines 部分,有一個節流的例子,它讓我理解了等待組是如何工作的。package mainimport (    "fmt"    "math/rand"    "sync"    "time")func main() {    c1 := make(chan int)    c2 := make(chan int)    go populate(c1)    go fanOutIn(c1, c2)    for v := range c2 {        fmt.Println(v)    }    fmt.Println("about to exit")}func populate(c chan int) {    for i := 0; i < 100; i++ {        c <- i    }    close(c)}func fanOutIn(c1, c2 chan int) {    var wg sync.WaitGroup    const goroutines = 10    wg.Add(goroutines)     for i := 0; i < goroutines; i++ {        go func() {            for v := range c1 {                func(v2 int) {                    c2 <- timeConsumingWork(v2)                }(v)            }            wg.Done()        }()    }    wg.Wait()    close(c2)}func timeConsumingWork(n int) int {    time.Sleep(time.Microsecond * time.Duration(rand.Intn(500)))    return n + rand.Intn(1000)}不符合我理解的部分是在fanOutIn我們設置WaitGroup, 和Add(10).為什么我打印出 100 個值?只能將單個值 ( i := 0) 放入c1,并且該值永遠不會從通道中顯式刪除。然后代碼命中wg.Done(),等待組隊列減少到 9,依此類推。根據我目前的理解,我希望看到 10 個值0 + rand.Intn(1000)。
查看完整描述

1 回答

?
一只甜甜圈

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

分拆出來的函數如下所示(包括go前面的 和調用它的括號):


go func() {

    for v := range c1 {

        func(v2 int) {

            c2 <- timeConsumingWork(v2)

        }(v)

    }

    wg.Done()

}()

這段代碼有點奇怪和離奇。讓我們進一步縮小它,丟棄wg.Done并只保留for循環本身:


for v := range c1 {

    func(v2 int) {

        c2 <- timeConsumingWork(v2)

    }(v)

}

有一個內部未命名的函數在這里非常沒用;我們可以在不改變程序行為的情況下丟棄它,得到:


for v := range c1 {

    c2 <- timeConsumingWork(v)

}

這最后是一個簡單的循環?,F在的一個關鍵問題是:您期望從這個循環中進行多少次迭代? 注意:它不一定是任何常數?;蛟S更好的方式來表達這個問題是:這個循環什么時候結束?


for循環讀取一個通道。這種循環在從通道讀取指示沒有更多數據時結束,即通道已關閉且其隊列為空。(請參閱循環的 Go 規范部分for。)


所以這個最里面的循環,for v := range c1直到通道c1關閉并且隊列中沒有更多數據時才會終止。該頻道是使用以下內容創建的:


c1 := make(chan int)

所以它沒有隊列,所以我們甚至不需要考慮這一點:它在 aclose(c1)關閉它后終止。 您現在應該尋找一個closecloses c1。


我們的近在哪里?

這是關閉的地方c1:


func populate(c chan int) {

    for i := 0; i < 100; i++ {

        c <- i

    }

    close(c)

}

我們稱之為 withc1作為它的參數,所以它的 final close(c)closes c1。現在你可以問:我們什么時候接到這個close電話? 答案很明顯:i >= 100在循環之后,即,在我們將 100 個值(分別從 0 到 99)發送到 channel 之后c1。


什么fanOutIn是剝離 10 個 goroutine。10 個 goroutine 中的每一個都運行我上面引用的第一個匿名函數。該匿名函數有一個循環,運行次數不定,一直重復直到通道c1關閉。循環中的每次行程都會獲取通道的值,因此最初,如果十個 goroutine 都設法在有任何可用值之前啟動,那么所有十個 goroutine 都將等待值。


當生產者函數將一個值放入通道時,十個等待的 goroutine 中的一個將獲取它并開始使用它。如果該 goroutine 需要很長時間才能回到自己for循環的頂部,則另一個 goroutine 將獲取下一個生成的值。所以這里發生的情況是,多達 10 個生成的值通過通道傳播到多達 10 個 goroutine。1 這些(最多 10 個)goroutine 中的每一個都花費了一些重要的時間來使用它的值,然后將最終產品值發送到通道c2并返回到它自己的無限for循環的頂部。


只有當生產者關閉了它的通道c(這里是我們的c1)時,十個 goroutine 才會看到一個關閉的通道空隊列,從而允許它們退出for循環。當他們退出他們的for循環時,他們每個人都會調用wg.Done()(一次)并終止。


因此,一旦close(c1)發生(通過close(c)in populate),最終所有十個匿名 goroutine 都將調用wg.Done(). 屆時,wg.Wait()infanOutIn將返回。這將調用close(c2)并從 中返回fanOutIn,同時終止該goroutine。


同時,在 中main,我們使用for v := range c2從通道中讀取c2。當十個 goroutine 中的任何一個for寫入值時,此循環將運行。c2只有當它自己關閉時它才會退出c2(它的隊列也必須是空的,但又c2是一個零長度隊列)。所以在關閉之前main不會繼續通過for循環,直到返回才會發生,直到發生十次調用才會發生,直到通道關閉才會發生。c2wg.Wait()wg.Done()c1


這意味著在調用之前main無法通過自己的for循環,并且只有在生成恰好 100 個值之后才會發生這種情況。populateclose(c)


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

添加回答

舉報

0/150
提交
取消
微信客服

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

幫助反饋 APP下載

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

公眾號

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