Go 优雅地停止收听频道的更好方法

Go 优雅地停止收听频道的更好方法,go,Go,我学习围棋。为了在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

我学习围棋。为了在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)
            }
        }
    }
}
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")
}

在这种情况下使用的一种常用方法是使用另一个通道来发送信号终止。从该通道读取将成为select中的一种情况。通常,当两个goroutine都编写完成时,您会关闭该通道。没有必要关闭这两个通道


在您的程序中,当没有一个通道关闭时,处理结束,而另一个通道的内容被丢弃,在某些情况下,实际上会将goroutine写入泄漏到该通道。如果该goroutine尚未完成写入,则它将阻止等待写入一个永远无法读取的通道。

这里的问题是:

“break”语句终止最内层“for”的执行, 同一函数中的“开关”或“选择”语句

因此,在退出
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")
}

转到操场链接。

相同的程序,但行数较少。我认为它更具可读性和清晰性

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
        }
    }
}

那怎么办?由于它们是两个独立的go例程,我如何知道何时完成这两个例程?请使用sync.WaitGroup
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