我是围棋的学习者。为了模拟Go中的并发性,我写了下面的例子:
package main
import (
"fmt"
"time"
)
func main() {
c1 := make(chan string)
c2 := make(chan string)
go func() {
for i := 0; i < 5; i++ {
time.Sleep(500 * time.Millisecond)
c1 <- "Every 500 ms"
}
close(c1)
}()
go func() {
for i := 0; i < 5; i++ {
time.Sleep(1000 * time.Millisecond)
c2 <- "Every 1 s"
}
close(c2)
}()
isDone := false
for !isDone {
select {
case msg1, ok := <-c1:
if !ok {
isDone = true
break
}
{
fmt.Println(msg1)
}
case msg2, ok := <-c2:
if !ok {
isDone = true
break
}
{
fmt.Println(msg2)
}
}
}
}
正如您所看到的,在主函数的无穷for循环中,我使用变量isDone
手动退出循环。但这种方法在我看来很麻烦。难道不可能在select
条件下进行信道测距吗?
上述代码的输出是(Go Playground Link(:
Every 500 ms
Every 1 s
Every 500 ms
Every 500 ms
Every 1 s
Every 500 ms
Every 500 ms
这里的问题是:
A";中断";语句终止执行最里面的";对于";,"开关";,或";选择";语句。
因此,在退出select
语句后,如果没有额外的检查,我们将无法退出for
循环。我已经更新了你的代码,以简化的方式实现这一点:
package main
import (
"fmt"
"time"
)
func main() {
c1 := make(chan string)
c2 := make(chan string)
go func() {
for i := 0; i < 5; i++ {
time.Sleep(500 * time.Millisecond)
c1 <- "Every 500 ms"
}
close(c1)
}()
go func() {
for i := 0; i < 5; i++ {
time.Sleep(1000 * time.Millisecond)
c2 <- "Every 1 s"
}
close(c2)
}()
ok := true
var msg1 string
var msg2 string
for {
select {
case msg1, ok = <-c1:
if !ok {
break
}
{
fmt.Println(msg1)
}
case msg2, ok = <-c2:
if !ok {
break
}
{
fmt.Println(msg2)
}
}
if !ok {
break
}
}
fmt.Println("For loop exited")
}
上面代码的Go操场链接在这里。
我知道这和你写的没有太大区别。可能会稍微简化一些。实现这一点的另一种方法是使用return
语句而不是break
,并在单独的方法中定义for - select
语句(假设可以(。查找以下示例:
package main
import (
"fmt"
"time"
)
func ForSelectMethod(c1 chan string, c2 chan string) {
for {
select {
case msg1, ok := <-c1:
if !ok {
return
}
{
fmt.Println(msg1)
}
case msg2, ok := <-c2:
if !ok {
return
}
{
fmt.Println(msg2)
}
}
}
}
func main() {
c1 := make(chan string)
c2 := make(chan string)
go func() {
for i := 0; i < 5; i++ {
time.Sleep(500 * time.Millisecond)
c1 <- "Every 500 ms"
}
close(c1)
}()
go func() {
for i := 0; i < 5; i++ {
time.Sleep(1000 * time.Millisecond)
c2 <- "Every 1 s"
}
close(c2)
}()
ForSelectMethod(c1, c2)
fmt.Println("For loop exited")
}
转到此处的游乐场链接。
相同的程序,但行数较少。我认为它更可读、更清晰。
package main
import (
"time"
)
func stop(tk ...*time.Ticker) {
for _, t := range tk {
t.Stop()
}
}
func main() {
ta := time.NewTicker(500 * time.Millisecond)
tb := time.NewTicker(1 * time.Second)
cta, ctb := 0, 0
for {
select {
case <-ta.C:
println("Every 500 ms")
cta++
case <-tb.C:
println("Every 1 s")
ctb++
}
if cta == 5 || ctb == 5 {
stop(ta, tb)
break
}
}
}
样本输出:
Every 500 ms
Every 500 ms
Every 1 s
Every 500 ms
Every 500 ms
Every 1 s
Every 500 ms
在这种情况下,常用的方法是使用另一个通道发出终止信号。从该通道读取成为选择中的一种情况。通常,当两个编写goroutine都完成时,您会关闭该通道。没有必要关闭这两个通道。
在您的程序中,当没有任何通道关闭时,处理结束,而另一个通道的内容被丢弃,在某些情况下,实际上会将goroutine写入泄漏到该通道。如果goroutine还没有完成写入,那么它将阻止等待写入一个永远不会读取的通道。