与 WaitGroup 进行通道同步。关闭通道和等待组

Sha*_*mad 4 go goroutine

我试图理解 Goroutines 中的同步。我这里有一个代码,在通道上写入从 0 到 4 的数字,完成后,我使用range并打印这些值从通道中读取。

当我在单独的 Goroutine 中等待使用wg.Wait()和关闭通道时,下面的代码工作正常。

package main

import (
    "fmt"
    "strconv"
    "sync"
)

func putvalue(i chan string, value string, wg *sync.WaitGroup) {
    i <- value
    defer wg.Done()
}

func main() {
    queue := make(chan string)
    var wg sync.WaitGroup

    for i := 0; i < 5; i++ {
        wg.Add(1)
        go putvalue(queue, strconv.Itoa(i), &wg)
    }

    go func() {
        wg.Wait()
        close(queue)
    }()

    for elem := range queue {
        fmt.Println(elem)
    }

}
Run Code Online (Sandbox Code Playgroud)

https://play.golang.org/p/OtaRP3Mm4lk

但是,如果我使用完全相同的代码,但在主线程中等待并关闭通道,则会导致死锁。下面的代码会导致死锁。

package main

import (
    "fmt"
    "strconv"
    "sync"
)

func putvalue(i chan string, value string, wg *sync.WaitGroup) {
    i <- value
    defer wg.Done()
}

func main() {
    queue := make(chan string)
    var wg sync.WaitGroup

    for i := 0; i < 5; i++ {
        wg.Add(1)
        go putvalue(queue, strconv.Itoa(i), &wg)
    }

    wg.Wait()
    close(queue)

    for elem := range queue {
        fmt.Println(elem)
    }

}
Run Code Online (Sandbox Code Playgroud)

https://play.golang.org/p/JXmdsdPKQPu

据我了解,在第二种情况下,主线程执行停止并等待,但这与在单独的 goroutine 中执行有什么不同?请帮助我理解这一点。

tor*_*rek 5

将每个 goroutine 视为一个单独的人(或 gopher: https: //blog.golang.org/gopher)可能会有所帮助。当你go f()得到一个新人/地鼠并给他们运行该功能的工作时。所以,你有 5 个额外的地鼠正在运行这个:

\n\n
func putvalue(i chan string, value string, wg *sync.WaitGroup) {\n    i <- value\n    defer wg.Done()\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

5 个人中的每一个跑到他们到达线的地方i <- value,然后停下来,等待一只地鼠跑到通道/邮箱的“获取”一侧,并把手伸过去去拿一根绳子,这是那种进入i渠道/邮箱的包裹。

\n\n

(旁白:defer wg.Done()应该是函数的第一wg.Done()行,而不是最后一行。或者,只是作为函数的最后一行。)

\n\n

现在,如果此时您获得了第六个额外的地鼠并让他这样做:

\n\n
{\n    wg.Wait()\n    close(queue)\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

他会在里面停下wg.Wait()来等待。

\n\n

现在,您的主要地鼠继续循环for。这从邮箱中读取,即,现在您的主要地鼠将手伸入频道的“获取”侧的邮箱/窗口中。五只等待的地鼠之一终于可以将他的绳子交到你的地鼠手中。你的主要 Gopher 获取字符串并将其带回你的for循环。被阻止的五只地鼠之一现在可以处决他的wg.Done()并死去(大概会去到退休地鼠的幸福之地)。

\n\n

您的主要地鼠继续循环for,通过邮箱获取更多包裹。当他这样做时,等待邮箱“put”的四只 gopher 完成并调用wg.Done(),这将工作组计数器递减计数。当计数为零时,不再有地鼠等待将包裹放入邮箱,但现在正在睡觉、等待的地鼠wg.Wait()被唤醒。所以他很快就会醒来并打电话close

\n\n

如果他还没有打电话close,你的主要地鼠就会陷入等待下一个包裹的状态。因此,只有剩下的一只地鼠可以做任何事情:他将完成关闭。或者,也许他醒得很快并且已经关闭了,但如果是这样,你的主要地鼠已经看到邮箱窗口永远关闭了。无论哪种方式,您的主要地鼠都已经看到或即将看到邮箱窗口\xe2\x80\x94通道\xe2\x80\x94已关闭,并且循环将停止并且您的主要地鼠将从和for返回main也前往快乐退休之地。

\n\n

不过,正如Burak Serdar 指出的,如果没有单独的地鼠执行wg.Wait()close则由主地鼠执行wg.Wait()。因此,他永远不会抽出时间for从(仍然打开的)邮箱/频道中读取内容。你的五只地鼠正在睡觉,等待一只地鼠把手伸进邮箱去取包裹。你的主要地鼠正在睡觉,等待计数器归零sync.WaitGroup。大家都睡了!

\n