与WaitGroup的频道同步。关闭通道和等待组

问题描述 投票:1回答:2

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

下面的代码在我使用wg.Wait()等待并在单独的Goroutine中关闭通道的地方很好用。

package main

import "fmt"
import "strconv"
import "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)
    }

}

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

package main

import "fmt"
import "strconv"
import "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)
    }

}

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

go goroutine
2个回答
3
投票

将每个goroutine视为一个单独的人(或gopher:https://blog.golang.org/gopher)可能会有所帮助。当您go f()时,您会得到一个新的人员/角色,并让他们执行该功能。因此,您有5个额外的地鼠正在运行:

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

[5个中的每一个都跑到到达i <- value线的点,然后停下来,等待地鼠跑到通道/邮箱的“获取”侧并伸出手来得到一个字符串,这是进入i通道/邮箱的包的类型。

((defer wg.Done()应该是函数的first行,而不是最后一行。或者,只需将wg.Done()做为函数的最后一行。)

现在,如果您现在获得第六只地鼠,让他去做:

{
    wg.Wait()
    close(queue)
}

他将停在wg.Wait()内部,等待。

您的主要地鼠现在继续进行到for循环。这是从邮箱中读取的,即现在​​您的main地鼠将手伸到通道“获取”侧的邮箱/窗口中。五个等待的地鼠中的一个终于可以将他的细绳放到您的地鼠的手中。您的主要Gopher接收字符串并将其带回到for循环。现在,被阻止的五个地鼠之一可以执行他的wg.Done()并过期(大概是退休的地鼠happy的幸福之地)。

您的主要gopher继续在for循环中,通过邮箱获取更多软件包。当他这样做时,等待邮箱“ put”上的四个地鼠结束并调用wg.Done(),这将工作组计数器递减计数。当计数达到零时,将不再有等待将软件包放入邮箱的地鼠,而正在等待wg.Wait()的正在睡眠的地鼠会被唤醒。因此,他很快就会醒来并致电close如果他还没有打电话给close,则您的主要地鼠被困在等待下一个包裹。因此,只有剩下的一只地鼠可以做任何事情:他会做关闭。或者,也许他很快醒来并已经关闭了,但是如果是这样,您的主要地鼠已经看到邮箱窗口永远关闭了。无论哪种方式,您的主要地鼠都将已经或将要看到,邮箱窗口(通道)已关闭,并且for循环将停止并且您的主要地鼠将从main返回,并且还可以到快乐退休之地。

不过是Burak Serdar noted,没有单独的地鼠先进行wg.Wait(),然后再进行close,这是您的[[main

地鼠来进行wg.Wait()。因此,他永远不会绕过for循环从(仍处于打开状态的)邮箱/频道进行读取。您的五个地鼠睡着了,等着地鼠把手伸过邮箱拿到他们的包裹。您的主要地鼠处于睡眠状态,等待sync.WaitGroup中的计数器下降到零。大家都睡着了!

1
投票
© www.soinside.com 2019 - 2024. All rights reserved.