Dog
Dog

Reputation: 7787

Break out of select loop?

I'm trying to use a select in a loop to receive either a message or a timeout signal. If the timeout signal is received, the loop should abort:

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

However, it doesn't seem to be stopping:

$ 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
[...]

Why? Am I using time.After wrong?

Upvotes: 38

Views: 40722

Answers (5)

Liqun
Liqun

Reputation: 4171

How about using some control variable to skip the loop? It's kind of hard or not so easy to understand with breaking label sometimes.

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

Upvotes: 12

Danie
Danie

Reputation: 454

I have the following solution, by using a anonymous function.

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

Upvotes: 10

OneOfOne
OneOfOne

Reputation: 99234

The "Go" way for that kind of situations is to use labels and break on the label, for example:

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

Ref:

Upvotes: 35

Martin Gallagher
Martin Gallagher

Reputation: 4814

In your example code, a return seems appropriate as Pat says, but for future reference you can use labels:

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

Upvotes: 13

Pat
Pat

Reputation: 906

The Go spec says:

A "break" statement terminates execution of the innermost "for", "switch", or "select" statement within the same function.

In your example you're just breaking out of the select statement. If you replace break with a return statement you will see that it's working.

Upvotes: 48

Related Questions