HFX
HFX

Reputation: 592

Is there a way to break the select if any channel is closed?

Let's say we are selecting on two channel. After running a few times, one of the channels is closed. How can I break out of the select?

package main

import (
    "fmt"
)

func main() {
    ch := make(chan int)
    closed := make(chan bool, 1)
    go func() {
        for i := 0; i < 5; i++ {
            ch <- 1
        }
        close(ch)
    }()

    for {
        select {
        case v := <-ch:
            fmt.Printf("v is %v\n", v)
        case <-closed:
            fmt.Println("The server is closed!")
            return
        }
    }
    fmt.Println("Break!")
}

It is blocked infinitely. How to make it work?

Upvotes: 3

Views: 7944

Answers (2)

Alexander Trakhimenok
Alexander Trakhimenok

Reputation: 6278

Here is working code that:

  1. Changed order of select-case. (according to the comment from @zzn this behavior is random and it's better not to rely on this - better to check if channel has been closed).
  2. Writes to the closed channel

https://play.golang.org/p/K83XcPbr7b

package main
import (
    "fmt"
)

func main() {
    ch := make(chan int)
    closed := make(chan bool)
    go func() {
        for i := 0; i < 5; i++ {
            ch <- i
        }
        close(ch)
    closed <- true
    }()

    for {
        select {
        case <- closed:
            fmt.Println("The server is closed!")
            return
        case v := <- ch:
            fmt.Printf("v is %v\n", v)
        }
    }
    fmt.Println("Break!")
}

If you don't change order of case there would be extra "zero" value: https://play.golang.org/p/JJaomKgqy8

Maybe it's better to check if channel has been closed?

https://play.golang.org/p/7Nd63b3JZ_

package main

import (
    "fmt"
)

func main() {
    ch := make(chan int)
    go func() {
        for i := 0; i < 5; i++ {
            ch <- i
        }
        close(ch)
    }()

    for {
        select {
        case v, hasMore := <- ch:
        if !hasMore {
            return
        }
            fmt.Printf("v is %v\n", v)
        }
    }
    fmt.Println("Break!")
}

Upvotes: 1

Kos
Kos

Reputation: 72241

You can return if the channel says it has no more items:

    case v, more := <-ch:
        if !more {
            return
        }
        fmt.Printf("v is %v\n", v)

Upvotes: 5

Related Questions