我试图理解 Goroutines 中的同步。我这里有一个代码,在通道上写入从 0 到 4 的数字,完成后,我使用range并打印这些值从通道中读取。
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 中执行有什么不同?请帮助我理解这一点。
将每个 goroutine 视为一个单独的人(或 gopher: https: //blog.golang.org/gopher)可能会有所帮助。当你go f()得到一个新人/地鼠并给他们运行该功能的工作时。所以,你有 5 个额外的地鼠正在运行这个:
func putvalue(i chan string, value string, wg *sync.WaitGroup) {\n i <- value\n defer wg.Done()\n}\nRun Code Online (Sandbox Code Playgroud)\n\n5 个人中的每一个跑到他们到达线的地方i <- value,然后停下来,等待一只地鼠跑到通道/邮箱的“获取”一侧,并把手伸过去去拿一根绳子,这是那种进入i渠道/邮箱的包裹。
(旁白:defer wg.Done()应该是函数的第一wg.Done()行,而不是最后一行。或者,只是作为函数的最后一行。)
现在,如果此时您获得了第六个额外的地鼠并让他这样做:
\n\n{\n wg.Wait()\n close(queue)\n}\nRun Code Online (Sandbox Code Playgroud)\n\n他会在里面停下wg.Wait()来等待。
现在,您的主要地鼠继续循环for。这从邮箱中读取,即,现在您的主要地鼠将手伸入频道的“获取”侧的邮箱/窗口中。五只等待的地鼠之一终于可以将他的绳子交到你的地鼠手中。你的主要 Gopher 获取字符串并将其带回你的for循环。被阻止的五只地鼠之一现在可以处决他的wg.Done()并死去(大概会去到退休地鼠的幸福之地)。
您的主要地鼠继续循环for,通过邮箱获取更多包裹。当他这样做时,等待邮箱“put”的四只 gopher 完成并调用wg.Done(),这将工作组计数器递减计数。当计数为零时,不再有地鼠等待将包裹放入邮箱,但现在正在睡觉、等待的地鼠wg.Wait()被唤醒。所以他很快就会醒来并打电话close。
如果他还没有打电话close,你的主要地鼠就会陷入等待下一个包裹的状态。因此,只有剩下的一只地鼠可以做任何事情:他将完成关闭。或者,也许他醒得很快并且已经关闭了,但如果是这样,你的主要地鼠已经看到邮箱窗口永远关闭了。无论哪种方式,您的主要地鼠都已经看到或即将看到邮箱窗口\xe2\x80\x94通道\xe2\x80\x94已关闭,并且循环将停止并且您的主要地鼠将从和for返回main也前往快乐退休之地。
不过,正如Burak Serdar 指出的,如果没有单独的地鼠执行wg.Wait(),close则由主地鼠执行wg.Wait()。因此,他永远不会抽出时间for从(仍然打开的)邮箱/频道中读取内容。你的五只地鼠正在睡觉,等待一只地鼠把手伸进邮箱去取包裹。你的主要地鼠正在睡觉,等待计数器归零sync.WaitGroup。大家都睡了!