Go trouble with buffered and unbuffered channels - go

I'm a bit confused about the difference between buffered and unbuffered channels in Go. For example, the below code executes fine:
package main
import "fmt"
func main() {
messages := make(chan string)
go func() { messages <- "ping" }()
msg := <-messages
fmt.Println(msg)
}
On the other hand, when I pass "ping" to messages in a regular function, there is a deadlock.
package main
import "fmt"
func main() {
messages := make(chan string)
func() { messages <- "ping" }()
msg := <-messages
fmt.Println(msg)
}
Finally, this is fixed when I use a buffered channel, like so
package main
import "fmt"
func main() {
messages := make(chan string, 1)
func() { messages <- "ping" }()
msg := <-messages
fmt.Println(msg)
}
I'm confused why the second case failed. Go By Example says that
By default channels are unbuffered, meaning that they will only accept sends (chan <-) if there is a corresponding receive (<- chan) ready to receive the sent value. Buffered channels accept a limited number of values without a corresponding receiver for those values.
In all three cases, isn't msg the sink for messages?

Unbuffered channels block if they are not read from. Buffered channels will not block until they hit capacity.
Your first example actually starts a separate go routine which executes the function that tries to write "ping" to the messages channel. It will block until the statement to read from the messages channel executes. The statement to read from the messages channel is able to be hit since the function is on a separate goroutine.
Your second example declares and calls a function which attempts to write to the messages channel, but that channel will never be ready to be written to since you are executing on the same main execution thread. The statement to read from the messages channel never hits since you are blocked on writing to the channel.
The third example, the channel is buffered and can be written to since it can accept 1 value before it blocks.

In the first example, the nested function is called from another goroutine. The function starts running and blocks waiting to write the channel. The main goroutine also runs, and reads from the channel, releasing the block in the second goroutine.
In the second example, the nested function is called by main, and main waits for it to return. Since the channel is not buffered, the write operation blocks, which means the main goroutine is blocked and there are no others, so deadlock.
In the third example, the channel is buffered, so the first write does not block.

You may have skimmed over a key word there: "if there is a corresponding receive (<- chan) ready to receive the sent value".
In your first example, the receiver is running concurrently to the sender, so when the sender sends, the receiver is ready to receive at that time. In the second example, they are not running concurrently, so when the sender sends, nothing is ready to receive (because the receive operation won't run until the anonymous function returns), and the send blocks.

Related

Why is the order of channels receiving causing/resolving a deadlock in Golang?

I've boiled my issue down to this simple example below. I am invoking a goroutine that takes two channels and sends one message to each. Then I am attempting to receive those messages further along. However, the order of channels receiving matters. If I use the same order I sent the messages, the program runs. If I switch, it does not.
I would have expected the goroutine to run independently from retrieving the messages, allowing me to receive from whichever channel I wanted to first.
I can solve this by sending messages to a single channel per goroutine (2 goroutines).
Could someone explain why there is an order dependence here and why 2 separate goroutines resolves that dependence?
package main
import "fmt"
func main() {
chanA := make(chan string)
chanB := make(chan string)
go func() {
chanA <- "el"
chanB <- "el"
}()
// if B is received before A, fatal error
// if A is received before B, completes
<-chanB
<-chanA
fmt.Println("complete")
}
You will need to buffer your channels. A buffered channel can store so many elements before it will block.
chanA := make(chan string, 1)
chanA <- "el" // This will not block
fmt.Println("Hello World")
When you do chanA <- "el" on the buffered channel above, the element gets placed into the buffer and the thread does not block. If you add a second element, it will then block as there is no room in the buffer:
chanA := make(chan string, 1)
chanA <- "el"
chanA <- "el" // <- This will block, as the buffer is full
In your example, you have a buffer of 0. So the first write to the channel is blocked, and requires another thread to read the value to unblock.
https://go.dev/play/p/6GbsVW4d0Mg
chanA := make(chan string)
go func() {
time.Sleep(time.Second)
fmt.Println("Pop:", <-chanA) // Unblock the writer
}()
chanA <- "el"
Extra knowledge
If you do not want a thread to block, you can wrap a channel insert in a select. This will ensure if the channel is full, your application does not deadlock. One cheap way of fixing this is a larger buffer...
https://go.dev/play/p/kKR-lrCO4FX
select {
case chanA <- "el":
default:
return fmt.Errorf("value not written: %s", value)
}
this is how goroutine works:
a goroutine will be blocked read/write on a channel unless if find another goroutine which write/read from the same channel.
Pay attention to read/write and write/read in the above blocked quote.
In your case, your anon goroutine(which you kicked off with go) waits to write on channelA until it finds a goroutine which reads from channelA.
The main goroutine waits to read from channelB unless it finds a goroutine that reads from it.
You can think it this way, any line written after read/write to channel won't be considered unless go finds another routine which write/read from the same channel.
So, if you change either read or write order you will not have deadlock or as you said another goroutine will do the job too.
Hope it's clear.
A write to or read from an unbuffered channel will block until there is a goroutine to write to or read from that channel. When the goroutine writes to a, it will block until the main goroutine can read from a, but main goroutine is also blocked waiting to read from b, hence deadlock.

How to reason about Go channel blocking in Go Concurrency Patterns fan-in example?

package main
import (
"fmt"
"math/rand"
"time"
)
func boring(msg string) <-chan string { // Returns receive-only channel of strings.
c := make(chan string)
go func() { // We launch the goroutine from inside the function.
for i := 0; ; i++ {
c <- fmt.Sprintf("%s %d", msg, i)
time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
}
}()
return c // Return the channel to the caller.
}
func fanIn(input1, input2 <-chan string) <-chan string {
c := make(chan string)
go func() {
for {
c <- <-input1
}
}()
go func() {
for {
c <- <-input2
}
}()
return c
}
func main() {
c := fanIn(boring("Joe"), boring("Ann"))
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
fmt.Println("You're both boring; I'm leaving.")
}
This is an example from Rob Pike's talk on Go Concurrency Patterns. I understand the idea behind the fan-in pattern and I understand that the order of messages printed in main is non-deterministic: we just print 10 messages that turn out to be ready.
What I do not completely understand, however, is the order of calls and what blocks what.
Only unbuffered channels are used so, as per the documentation, an unbuffered channel blocks the sender.
The boring function launches a goroutine that sends strings to the unbuffered channel c, which is returned. If I understand correctly, this inner goroutine is launched but doesn't block boring. It can immediately return the channel in main to the fanIn function. But fanIn does almost the same thing: it receives the values from the input channel and sends them to its own channel that is returned.
How does the blocking happen? What blocks what in this case? A schematic explanation would be perfect because, honestly, even though I have an intuitive understanding, I would like to understand the exact logic behind it.
My intuitive understanding is that each send inside boring blocks until the value is received in fanIn, but then the value is immediately sent to another channel so it gets blocked until the value is received in main. Roughly speaking, the three functions are tightly bound to each other due to the use of channels
How does the blocking happen? What blocks what in this case?
Each send on an unbuffered channel blocks if there is no corresponding receive operation on the other side (or if the channel is nil, which becomes a case of having no receiver).
Consider that in main the calls to boring and fanIn happen sequentially. In particular this line:
c := fanIn(boring("Joe"), boring("Ann"))
has order of evaluation:
boring("Joe")
boring("Ann")
fanIn
The send operations in boring("Joe") and boring("Ann") have a corresponding receive operation in fanIn, so they would block until fanIn runs. Hence boring spawns its own goroutine to ensure it returns the channel before fanIn can start receiving on it.
The send operations in fanIn have then a corresponding receive operation in main, so they would block until fmt.Println(<-c) runs. Hence fanIn spawns its own goroutine(s) to ensure it returns the out channel before main can start receiving on it.
Finally main's execution gets to fmt.Println(<-c) and sets everything in motion. Receiving on c unblocks c <- <-input[1|2], and receiving on <-input[1|2] unblocks c <- fmt.Sprintf("%s %d", msg, i).
If you remove the receive operation in main, main can still proceed execution and the program exits right away, so no deadlock occurs.

Go channel readyness

I am trying to understand channels in Go. I have read that by default sends and receives block until both the sender and receiver are ready. But how do we figure out readyness of sender and receiver.
For example in the following code
package main
import "fmt"
func main() {
ch := make(chan int)
ch <- 1
fmt.Println(<-ch)
}
The program will get stuck on the channel send operation waiting forever for someone to read the value. Even though we have a receive operation in println statement it ends up in a deadlock.
But for the following program
package main
import "fmt"
func main() {
ch := make(chan int)
go func () {
ch <- 1
}()
fmt.Println(<-ch)
}
The integer is passed successfully from go routine to main program. What made this program work? Why second works but first do not? Is go routine causing some difference?
Let's step through the first program:
// My notes here
ch := make(chan int) // make a new int channel
ch <- 1 // block until we can send to that channel
// keep blocking
// keep blocking
// still waiting for a receiver
// no reason to stop blocking yet...
// this line is never reached, because it blocks above forever.
fmt.Println(<-ch)
The second program splits the send off into its own line of execution, so now we have:
ch := make(chan int) // make a new int channel
go func () { // start a new line of execution
ch <- 1 // block this second execution thread until we can send to that channel
}()
fmt.Println(<-ch) // block the main line of execution until we can read from that channel
Since those two lines of execution can work independently, the main line can get down to fmt.Println and try and receive from the channel. The second thread will wait to send until it has.
The go routine absolutely makes a difference. The go routine that writes to the channel will be blocked until your main function is ready to read from the channel in the print statement. Having two concurrent threads, one that reads and one that writes fulfills the readiness on both sides.
In your first example, the single thread gets blocked by the channel write statement and will never reach the channel read.
You need to have a concurrent go routine to read from a channel whenever you write to it. Concurrency goes hand-in-hand with channel usage.

How channel buffers work in golang when used without goroutines (within their scope) vs when they are passed to goroutines?

I am absolute newbie in Golang. I am learning through Tour of Go and then implementing ideas with my own understanding. I am having problem with goroutines. I made a unbuffered channel , then sent a string to that channel.
func main() {
p := make(chan string)
p <- "Hello goroutine"
fmt.Println(<-p)
}
throws error
fatal error: all goroutines are asleep - deadlock!
I get it, channel is unbuffered. (That's the reason. Right?).
But when I refactor p <- "Hello goroutine to a goroutine
func main() {
p := make(chan string)
go sendHello(p)
fmt.Println(<-p)
}
func sendHello(p chan string) {
p <- "Hello goroutine"
}
It works without problem. I read that we do not need to use pointers with maps,slices and channels with most cases to modify the value. Was channel p passed to func sendHello(p chan string) via a copy which had a separate buffer. I still can not get my head around it.
Keep in mind that a channel has two ends, a sender and a receiver. Your problem is about the order of execution.
In the first example, when you use an unbuffered channel, the channel expects a receiver, while there's none as of sending Hello goroutine message, and waits until there's one (this is not the case for a buffered channel as it doesn't require to wait), and the execution never reaches the next line (i.e. a deadlock).
But in the second example, the receiver is bound to the channel and the groutine is executed after, and both sender and receiver don't remain in a wait state.

Golang: cannot send on channel

Why is it not sending on the channel and blocking the execution? How can I make this constellation work so that I can send a signal into MoneyDive() and continue execution?
package main
import (
"fmt"
)
type Quack func(ch chan bool)
type DagobertDuck struct {
quack Quack
}
func (self *DagobertDuck) MoneyDive() {
ch := make(chan bool)
self.quack(ch)
b := <-ch
if b {
fmt.Println("true")
} else {
fmt.Println("false")
}
}
func mockQuack(ch chan bool) {
fmt.Println("mockQuack start")
ch <- true
fmt.Println("mockQuack done")
}
func main() {
dd := DagobertDuck{quack: mockQuack}
dd.MoneyDive()
}
https://play.golang.org/p/1omlb7u6-A
Because you have an unbuffered channel, and you can only send a value on an unbuffered channel without blocking if there is another goroutine which is ready to receive from it.
Since you only have 1 goroutine, it gets blocked. Solution is simple: launch your Quack.quack() method in a new goroutine:
go self.quack(ch)
Then the output (try it on the Go Playground):
mockQuack start
mockQuack done
true
Another option is to not launch a new goroutine but make a buffered channel, so it can hold some values without any receivers ready to receive from it:
ch := make(chan bool, 1) // buffered channel, buffer for 1 value
This creates a channel which is capable of "storing" one value without any receivers ready to receive it. A second send on the channel would also block, unless the value is received from it first (or a receiver ready to receive a value from it).
Try this buffered channel version on the Go Playground.
Relevant section from the spec: Send statements:
Both the channel and the value expression are evaluated before communication begins. Communication blocks until the send can proceed. A send on an unbuffered channel can proceed if a receiver is ready. A send on a buffered channel can proceed if there is room in the buffer. A send on a closed channel proceeds by causing a run-time panic. A send on a nil channel blocks forever.
Notes:
Based on the received value you print true or false. This can be done with a single line, without the if statement:
fmt.Println(b)
You can even get rid of the b local variable, and print the received value right away:
fmt.Println(<-ch)
Also I assume you used channels because you wanted to play with them, but in your case mockQuack() could simply return the bool value, without the use of channels.

Resources