-1

According to Go documentation:

Receivers always block until there is data to receive

This test should fail, because for last receiving operation from a channel there is no corresponding write :

package main

import "fmt"

func main() {
    c := make(chan int)    
    for i := 0; i < 4; i++ { // 4 async reads
      go func() {
            fmt.Println("received:", <-c)
         }()

    }    
    // just 3 writes, 1 write is missing
    c <- 1   
    c <- 2 
    c <- 3   
}

However script not fails with error message in read goroutine, but it succeeds printing 3 values :

received: 1
received: 2
received: 3

Why it is so, or I misunderstood something about synchronization ?

Agnius Vasiliauskas
  • 10,935
  • 5
  • 50
  • 70
  • 2
    "This test should fail, because for last receiving operation from a channel there is no corresponding write" and why should the test fail? It is true that "there is no corresponding write" but the test does literally _nothing_ to check that any attempted receive on the channel is fulfilled. – Volker Jan 15 '20 at 11:43
  • In my opinion at least run-time could inform the user how much goroutines are terminated upon exiting program without data being received. I.e. I expect informational message. I think it would have added value – Agnius Vasiliauskas Jan 15 '20 at 12:12
  • Your opinion does not match how Go works unfortunately. – Adrian Jan 15 '20 at 15:02

1 Answers1

4

There is no deadlock here because the main goroutine is not blocked. It sends 3 values on c which succeed because there are 4 launched goroutines receiving from it, and then it ends. And with it ends your app as well, it does not wait for other non-main goroutines to end. See No output from goroutine.

A deadlock implies all goroutines to be blocked. That's not the case here.

It is not an error to try to receive from a channel where there is no one (currently or ever) ready to send on. If fact, that's completely normal. That's one of the use-cases of channels: it acts as a synchronization tool, you can send / receive and the operation will block until the other end is ready as well.

There are cases when even a goroutine blocked for the entire app lifetime is normal, e.g. a goroutine may wait for user input such as CTRL+BREAK, which the user may never press and the app may end normally.

So this is not considered an error, and no error or warning messages are printed for these. But if you're curious, it's easy to implement it. Just add a deferred function to your main() which will be called last thing before your main() function (and your app with it) ends. In that print the number of running goroutines:

func main() {
    defer func() {
        fmt.Println("Remaining goroutines:", runtime.NumGoroutine()-1) //-1 for main
    }()

    // your code
}

With this addition, output will be:

received: 1
received: 2
received: 3
Remaining goroutines: 1

If you change your loop to launch 14 goroutines instead of 1, output will say there are 11 remaining goroutines.

Last note: since in your app the main() function does not wait for other goroutines to end, they may still be active at the time when deferred functions are called, so they may or may not be included in the remaining goroutines count. If you would use e.g. sync.WaitGroup to wait for them to end, then they certainly wouldn't be included. See Prevent the main() function from terminating before goroutines finish in Golang for an example.

icza
  • 389,944
  • 63
  • 907
  • 827
  • But READ goroutine must be blocked, because 1 write is missing. But still there is no any error indicating about blocked reading routine – Agnius Vasiliauskas Jan 15 '20 at 11:07
  • @AgniusVasiliauskas Yes, a goroutine is blocked until the app ends, so what? It's not an error, and it's not a deadlock. – icza Jan 15 '20 at 11:08
  • I changed deadlock into "error". Its not the point here. The point is why there is no error/information messages that some goroutines are blocked. To my view this program is invalid, at least some messages about blocked routines must be generated. Why it is not so ? – Agnius Vasiliauskas Jan 15 '20 at 11:10
  • A single goroutine out of many being blocked is normal. Go does not try to predict the future: the goroutine might be unblocked in an hour, a day or a year. Besides, at that point `main()` exits, so neither `fmt.Println(3)` nor the deadlock detector is guaranteed to execute. – justinas Jan 15 '20 at 11:11
  • @justinas Your point has some good sides, but if some goroutine is blocked in a **whole** execution life cycle of a program, doesn't it indicates that there may be an error so that user deserved to know about it ? – Agnius Vasiliauskas Jan 15 '20 at 11:15
  • 1
    @AgniusVasiliauskas No, it does not. It just indicates that the condition it was waiting for never happened during the life cycle of the program. A goroutine may be waiting for events from the user (e.g. `ctrl+break` key), but a user may never press those keys, and the app may end normally. – icza Jan 15 '20 at 11:18
  • @icza, Indeed some scenarios may be positive ones. However in my opinion at least run-time could inform the user how much goroutines are terminated without data received. I.e. I expect informational message. I think it would have added value – Agnius Vasiliauskas Jan 15 '20 at 12:10
  • @AgniusVasiliauskas See edited answer, showed how you can make such info printed. – icza Jan 15 '20 at 12:16