In the Go select construct, can I have send and receive to unbuffered channel in two case statements?

Viewed 4143

I am expecting this code to go in an infinite loop, sending and receiving the message. But it does not seem to either send or receive. Why?

go func() {
    for {
        select {
        case ch1 <- 1:
            println("sent 1 ")
        case c := <-ch1:
            println(" received ", c)
        }
        time.Sleep(time.Second)
    }
}()
2 Answers

The channel documentation says:

If the capacity is zero or absent, the channel is unbuffered and communication succeeds only when both a sender and receiver are ready.

The select documentation says:

If one or more of the communications can proceed, a single one that can proceed is chosen via a uniform pseudo-random selection.

The receive case cannot proceed because there's not a ready sender. The send case cannot proceed because there's not a ready receiver. The program deadlocks in the select.

A single goroutine cannot make the send and receive both ready because the goroutine can only execute one branch of the select at a time.

The program will loop forever if the channel is buffered (playground example).

To unbuffered channel with one goroutine: No (Why: by design):
You may use Buffered channel with one goroutine or
two goroutines using unbuffered channel to avoid deadlock:

If the channel is unbuffered, the sender blocks until the receiver has received the value. If the channel has a buffer, the sender blocks only until the value has been copied to the buffer; if the buffer is full, this means waiting until some receiver has retrieved a value.


1- Using buffered channel: The main itself is a goroutine and if you need do it in one goroutine you should use buffered channel like this:

package main

import (
    "fmt"
    "time"
)

func main() {
    ch1 := make(chan int, 1)
    for {
        select {
        case ch1 <- 1:
            fmt.Println("sent 1 ")
        case c := <-ch1:
            fmt.Println(" received ", c)
        }
        time.Sleep(time.Second)
    }
}

output:

sent 1
 received  1
sent 1
 received  1
sent 1
 received  1

2- Using unbuffered channel: You may send and receive to unbuffered channel in two case statements in one select, and you need two goroutines here, see:

Try this:

package main

import (
    "fmt"
    "time"
)

func main() {
    go ct("Alex")
    go ct("John")
    //select {}
    time.Sleep(300 * time.Millisecond)
}
func ct(name string) {
    for {
        select {
        case ch1 <- 1:
            fmt.Println(name, "sent 1")
        case c := <-ch1:
            fmt.Println(name, "received:", c)
        }
        fmt.Println(name, "waiting...")
        time.Sleep(100 * time.Millisecond)
    }
}

var ch1 = make(chan int)

output:

John sent 1
John waiting...
Alex received: 1
Alex waiting...
John received: 1
John waiting...
Alex sent 1
Alex waiting...
John sent 1
John waiting...
Alex received: 1
Alex waiting...
Related