打破选择循环?

Dog*_*Dog 6 concurrency select go goroutine

我正在尝试select在循环中使用a 来接收消息或超时信号.如果收到超时信号,则循环应该中止:

package main
import ("fmt"; "time")
func main() {
    done := time.After(1*time.Millisecond)
    numbers := make(chan int)
    go func() {for n:=0;; {numbers <- n; n++}}()
    for {
        select {
            case <-done:
                break
            case num := <- numbers:
                fmt.Println(num)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,它似乎没有停止:

$ go run a.go
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
[...]
3824
3825
[...]
Run Code Online (Sandbox Code Playgroud)

为什么?我用time.After错了吗?

One*_*One 19

这种情况的“Go”方式是使用标签并在标签上打断,例如:

L:
    for {
        select {
            case <-done:
                break L
            case num := <- numbers:
                fmt.Println(num)
        }
    }
Run Code Online (Sandbox Code Playgroud)

参考:


Pat*_*Pat 8

Go 规范说:

"break"语句终止在同一函数内执行最里面的"for","switch"或"select"语句.

在你的例子中,你只是打破了select语句.如果替换breakreturn语句,您将看到它正在运行.

  • 但是为什么 `break` 在 `select`/`switch` 中执行任何操作呢?我认为它们默认已经突破了,这就是为什么有“fallthrough”关键字? (2认同)

Dav*_*vid 8

使用一些控制变量来跳过循环怎么样?有时打破标签会很难或不太容易理解。

package main
import ("fmt"; "time")
func main() {
    done := time.After(1*time.Millisecond)
    numbers := make(chan int)
    go func() {for n:=0;; {numbers <- n; n++}}()
    completed := false
    for !completed {
        select {
            case <-done:
                completed = true
                // no break needed here
            case num := <- numbers:
                fmt.Println(num)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Mar*_*her 7

在您的示例代码中,return似乎适合Pat说,但为了将来参考,您可以使用标签:

package main

import (
    "fmt"
    "time"
)

func main() {
    done := time.After(1 * time.Millisecond)
    numbers := make(chan int)

    // Send to channel
    go func() {
        for n := 0; ; {
            numbers <- n
            n++
        }
    }()

readChannel:
    for {
        select {
        case <-done:
            break readChannel
        case num := <-numbers:
            fmt.Println(num)
        }
    }

    // Additional logic...
    fmt.Println("Howdy")
}
Run Code Online (Sandbox Code Playgroud)

  • 一个更具可读性的替代方案就在眼前,它是[将循环分隔为单独的函数](/sf/answers/3421574271/),其中包含“return”。Return 比“break label”更具可读性。如果它是一个“货物崇拜”,请提供一个来自真实代码的示例,其中在删除函数并引入标签后,带有“return”的单独函数将变得更容易理解:我无法在任何 Go 代码中想到一个到目前为止,我看到了很多可以通过引入函数来改进的地方。 (2认同)

小智 7

通过使用匿名函数,我有以下解决方案。

    func() {
    for {
        select {
        case <-time.After(5 * time.Second):
            if token := c.Connect(); token.Wait() && token.Error() != nil {
                fmt.Println("connect err:", token.Error())
            } else {
                fmt.Println("breaking")
                return
            }
        }
    }
    }()
Run Code Online (Sandbox Code Playgroud)