为什么这里有通道变量输出
Why variable output here with channels
我正在尝试从 here 修改的代码。我创建了 5 个通道并发送了 5 次数据:
package main
import "fmt"
func greet(c chan string) {
fmt.Println("Hello " + <-c + "!")
}
func main() {
fmt.Println("main() started")
c := make(chan string)
for i:=0; i<5; i++ {
go greet(c)
}
c <- "AAA"
c <- "BBB"
c <- "CCC"
c <- "DDD"
c <- "EEE"
fmt.Println("main() stopped")
}
我希望打印所有 5 个字符串。但是,我发现可变输出。一些输出是:
$ ./rnchannel
main() started
Hello AAA!
Hello DDD!
Hello BBB!
Hello CCC!
Hello EEE!
main() stopped
$ ./rnchannel
main() started
Hello CCC!
Hello DDD!
main() stopped
$ ./rnchannel
main() started
Hello CCC!
Hello BBB!
Hello AAA!
Hello DDD!
main() stopped
为什么打印的行数可变?
您不会等到所有字符串都打印出来才退出。一旦主线程执行结束,它就会关闭所有 goroutine 并结束程序。由于这是同时发生的,因此无法确定允许打印多少个字符串。
。当 main()
退出时,所有 goroutines 都会被杀死。不能保证你的其他 goroutines 会在那之前完成,这就是并发的本质。修复方法如下。
首先,让我们对 greet
进行一些更改。让它睡一会儿,使问题更加明显。我们还将让它接受一个字符串而不是通道,我们稍后会看到原因。
func greet(str string) {
time.Sleep(100 * time.Millisecond)
fmt.Println("Hello " + str + "!")
}
我们不需要创建一堆 goroutine 来从通道读取固定次数,而是需要一个 goroutine 从通道读取直到它用完。使用 range
最容易做到这一点。这充分利用了渠道。
我们还需要一种方法来告诉主程序等待循环完成。 This is easiest done with a second channel. More complex synchronization uses WaitGroups.
c := make(chan string, 2)
done := make(chan bool, 1)
go func() {
for str := range(c) {
greet(str)
}
done <- true
}()
goroutine 将从 c
开始读取,直到它关闭。然后它将 true
发送到频道 done
。两者都被缓冲以避免由于阻塞等待读取或写入通道而导致的死锁。
回到 main
,我们写入通道,显式关闭它,然后等待从 done
.
读取
c <- "AAA"
c <- "BBB"
c <- "CCC"
c <- "DDD"
c <- "EEE"
close(c)
<-done
fmt.Println("main() stopped")
<-done
将阻塞,直到有内容可读为止。这允许 goroutine 完成。
并将它们整合在一起。
package main
import(
"fmt"
"time"
)
func greet(str string) {
time.Sleep(100 * time.Millisecond)
fmt.Println("Hello " + str + "!")
}
func main() {
fmt.Println("main() started")
c := make(chan string, 2)
done := make(chan bool, 1)
go func() {
for str := range(c) {
greet(str)
}
done <- true
}()
c <- "AAA"
c <- "BBB"
c <- "CCC"
c <- "DDD"
c <- "EEE"
close(c)
<-done
fmt.Println("main() stopped")
}
我正在尝试从 here 修改的代码。我创建了 5 个通道并发送了 5 次数据:
package main
import "fmt"
func greet(c chan string) {
fmt.Println("Hello " + <-c + "!")
}
func main() {
fmt.Println("main() started")
c := make(chan string)
for i:=0; i<5; i++ {
go greet(c)
}
c <- "AAA"
c <- "BBB"
c <- "CCC"
c <- "DDD"
c <- "EEE"
fmt.Println("main() stopped")
}
我希望打印所有 5 个字符串。但是,我发现可变输出。一些输出是:
$ ./rnchannel
main() started
Hello AAA!
Hello DDD!
Hello BBB!
Hello CCC!
Hello EEE!
main() stopped
$ ./rnchannel
main() started
Hello CCC!
Hello DDD!
main() stopped
$ ./rnchannel
main() started
Hello CCC!
Hello BBB!
Hello AAA!
Hello DDD!
main() stopped
为什么打印的行数可变?
您不会等到所有字符串都打印出来才退出。一旦主线程执行结束,它就会关闭所有 goroutine 并结束程序。由于这是同时发生的,因此无法确定允许打印多少个字符串。
main()
退出时,所有 goroutines 都会被杀死。不能保证你的其他 goroutines 会在那之前完成,这就是并发的本质。修复方法如下。
首先,让我们对 greet
进行一些更改。让它睡一会儿,使问题更加明显。我们还将让它接受一个字符串而不是通道,我们稍后会看到原因。
func greet(str string) {
time.Sleep(100 * time.Millisecond)
fmt.Println("Hello " + str + "!")
}
我们不需要创建一堆 goroutine 来从通道读取固定次数,而是需要一个 goroutine 从通道读取直到它用完。使用 range
最容易做到这一点。这充分利用了渠道。
我们还需要一种方法来告诉主程序等待循环完成。 This is easiest done with a second channel. More complex synchronization uses WaitGroups.
c := make(chan string, 2)
done := make(chan bool, 1)
go func() {
for str := range(c) {
greet(str)
}
done <- true
}()
goroutine 将从 c
开始读取,直到它关闭。然后它将 true
发送到频道 done
。两者都被缓冲以避免由于阻塞等待读取或写入通道而导致的死锁。
回到 main
,我们写入通道,显式关闭它,然后等待从 done
.
c <- "AAA"
c <- "BBB"
c <- "CCC"
c <- "DDD"
c <- "EEE"
close(c)
<-done
fmt.Println("main() stopped")
<-done
将阻塞,直到有内容可读为止。这允许 goroutine 完成。
并将它们整合在一起。
package main
import(
"fmt"
"time"
)
func greet(str string) {
time.Sleep(100 * time.Millisecond)
fmt.Println("Hello " + str + "!")
}
func main() {
fmt.Println("main() started")
c := make(chan string, 2)
done := make(chan bool, 1)
go func() {
for str := range(c) {
greet(str)
}
done <- true
}()
c <- "AAA"
c <- "BBB"
c <- "CCC"
c <- "DDD"
c <- "EEE"
close(c)
<-done
fmt.Println("main() stopped")
}