0
package main

import (
    "fmt"
    "time"
)

func main() {

    ch := make(chan int)
    go func() {
        fmt.Printf("func at %d\n", time.Now().UnixNano())
        select {
        case ch <- 1:
            fmt.Println("running send")
        default:
            fmt.Println("running default")
        }
    }()

    time.Sleep(100 * time.Millisecond)
    fmt.Printf("main at %d\n", time.Now().UnixNano())
    fmt.Println(<-ch)
}

the playground here

I have researched for a day but still cannot explain why case ch <- 1: is not ready, and the default case is selected to run. Of course, it causes deadlock!

phuocph
  • 121
  • 1
  • 8
  • f you're waiting for a value that may come later, why do you have a `default` case? – JimB May 03 '20 at 12:54

2 Answers2

2

From doc:

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.

One way is to use this. Here's the receiver goroutine is spawned first. Also, if the receiver is not ready yet, default case will be picked up. And if it's ready, the specific case will be ready. If you run this several times, you can see either of cases happening.

package main

import (
    "fmt"
    "time"
)

func main() {
    ch := make(chan int)
    // goroutine acts as the reciever
    go func() {
        fmt.Printf("main at %d\n", time.Now().UnixNano())
        fmt.Println(<-ch)
    }()
    go func() {
        fmt.Printf("func at %d\n", time.Now().UnixNano())
        select {
        case ch <- 1:
            fmt.Println("running send")
        default:
            fmt.Println("running default")
        }
    }()
    time.Sleep(1 * time.Second) // Wait for the goroutines
}

Another solution would be to use, buffered channel:

package main

import (
    "fmt"
    "time"
)

func main() {

    ch := make(chan int, 1)
    go func() {
        fmt.Printf("func at %d\n", time.Now().UnixNano())
        select {
        case ch <- 1:
            fmt.Println("running send")
        default:
            fmt.Println("running default")
        }
    }()

    time.Sleep(100 * time.Millisecond)
    fmt.Printf("main at %d\n", time.Now().UnixNano())
    fmt.Println(<-ch)
}

Also, do read this thread on stackoverflow

shmsr
  • 3,802
  • 2
  • 18
  • 29
  • As I understand your answer, both `case ch <- 1` and `default:` is ready, select pick `default:` randomly. That's why `fmt.Println(<-ch)` causes deadlock. Is it you meaning? – phuocph May 03 '20 at 10:46
  • 1
    Yes and no. Point is in your code `case ch <- 1` doesn't get ready because of the explanation I gave related to unbuffered channel. And yes, selection of default and use of `fmt.Println(<-ch)` creates a deadlock state. – shmsr May 03 '20 at 10:50
  • 1
    I've edited the answer. Please have a look. And run the first program several times to see both cases. You'll understand how the spawning of goroutines matters. – shmsr May 03 '20 at 10:56
  • As you said, I try to sleep a lit bit before `fmt.Printf("main at %d\n", time.Now().UnixNano())` => running default first, then `fmt.Printf("func at %d\n", time.Now().UnixNano())` => running send. Your explanation is true. I wonder why `case ch <- 1` is not ready. I though it's always ready because no other senders block it. – phuocph May 03 '20 at 11:26
  • Actually, my question come from [Go Concurrency Patterns: Timing out, moving on](https://blog.golang.org/concurrency-timeouts) in the Query function. The author mention "_However, if the result arrives before the main function has made it to the receive, the send could fail since no one is ready_". I cannot find out why – phuocph May 03 '20 at 11:28
  • 1
    See, your channel is unbuffered which means if the sender sends into the channel, there's no buffer to store it and hence there should be a receiver ready. Having a buffered channel will guarantee that its channel has some buffer to store it. So, the send could fail since no one is ready means that if the result arrives before the main goroutine has made it to the receive, the send could fail. – shmsr May 03 '20 at 12:32
  • 1
    Read this as well: https://stackoverflow.com/a/47525732/5821408 – shmsr May 03 '20 at 12:41
  • Your first example does not guarantee anything, as there no coordination ensuring the value is sent before the select statement. Maybe that's the case you were trying to show, but it does not make for a correct answer. – JimB May 03 '20 at 12:48
  • As I know, the send channel is ready when the is a receiver is waiting to receive the value, the receive channel is ready when the is a send channel is sending to it and waiting the sent value to be receive. Is it right? – phuocph May 03 '20 at 13:19
0

You have created the Go channel with make(chan int) which is not buffered. You need a buffered channel (that won't necessarily block), you should use make(chan int, 20) where 20 is the size of the channel

The thing about unbuffered channels is that they are also synchronous, so they always block on write as well as read. The same thing can happen to buffered channels when the buffer fills up.

Try below code

package main

import (
    "fmt"
    "time"
)

func main() {

    ch := make(chan int, 20)
    go func() {
        fmt.Printf("func at %d\n", time.Now().UnixNano())
        select {
        case ch <- 1:
            fmt.Println("running send")
        default:
            fmt.Println("running default")
        }
    }()

    time.Sleep(100 * time.Millisecond)
    fmt.Printf("main at %d\n", time.Now().UnixNano())
    fmt.Println(<-ch)
}
Puneet Singh
  • 3,477
  • 1
  • 26
  • 39
  • Can you explain a lit bit more. I know buffered channel can solve the issue, I also know locking technique for buffered and unbuffered channel. But, I cannot explain what happens here. – phuocph May 03 '20 at 10:24