我试图了解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中执行它有何不同?请帮助我理解这一点。
将每个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
中的计数器下降到零。大家都睡着了!