简体   繁体   中英

Go trouble with buffered and unbuffered channels

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM