Home >Backend Development >Golang >Understanding golang blocking channel behavior by writing multiple times to the channel
In this article, editor Youzi will introduce to you how to understand the behavior of golang blocking the channel by writing multiple times to the channel. In golang, channels are an important mechanism for passing data between coroutines. When a channel is full, write operations are blocked until the channel becomes free. We'll demonstrate this behavior with a simple example and explain how blocking channels work and how to use them. Whether you are a beginner or an experienced golang developer, you can gain useful knowledge and practical experience from this article. let's start!
I am new to golang and trying to understand concurrency in the language. I have a code that pushes some values to a channel and then reads them.
package main import ( "log" "time" ) func Greet2(c chan string) { // logging to Stdout is not an atomic operation // so artificially, sleep for some time time.Sleep(2 * time.Second) // 5. until below line reads and unblock the channel log.Printf("5. Read Greet2:: %s\n\n", <-c) } func Greet(c chan string) { // 4. Push a new value to the channel, this will block // Process will look for other go routines to execute log.Printf("4. Add 'Greet::John' to the channel, block until it is read. Remember, 'Greet' goroutine will block and only other goroutines can run even though this go routine can pull the value out from the channel.\n\n") c <- "Greet::John!" // 8. This statement will never execute log.Printf("8. Read Greet:: %s !\n\n", <-c) } func main() { c := make(chan string) log.Println("1. Main start") // 2. Both go routine will be declared and both will // for a value to be inserted in the channel log.Println("2. Declare go routines.\n\n") go Greet(c) go Greet2(c) // 3. write will block log.Println("3. Add 'main::Hello' to the channel, block until it is read. Remember, 'main' goroutine will block and only other goroutines can run even though this go routine can pull the value out from the channel.\n\n") c <- "main::Hello" // Sleep to give time goroutines to execute time.Sleep(time.Second) // 6. read the channel value. log.Printf("6. Read main:: %s \n\n", <-c) // 7. Insert a new value to the channel log.Println("7. Add 'main::Bye' to the channel, block until it is read.\n") c <- "main::Bye" // Sleep to give time goroutines to execute time.Sleep(time.Second) log.Println("9. Main stop") }
The output of the above program is
2023/09/02 21:58:07 1. Main start 2023/09/02 21:58:07 2. Declare go routines. 2023/09/02 21:58:07 3. Add 'main::Hello' to the channel, block until it is read. Remember, 'main' goroutine will block and only other goroutines can run even though this go routine can pull the value out from the channel. 2023/09/02 21:58:07 4. Add 'Greet::John' to the channel, block until it is read. Remember, 'Greet' goroutine will block and only other goroutines can run even though this go routine can pull the value out from the channel. 2023/09/02 21:58:10 5. Read Greet2:: main::Hello 2023/09/02 21:58:11 6. Read main:: Greet::John! 2023/09/02 21:58:11 7. Add 'main::Bye' to the channel, block until it is read. 2023/09/02 21:58:11 8. Read Greet:: main::Bye ! 2023/09/02 21:58:12 9. Main stop
I can't understand why 4.
(another write to channel) is executed before 5.
(first read from channel) because 3.
will block and the channel will be unavailable until the value is read from it (in step 5.
). Did I misunderstand the blocking behavior, in step 3.
there is only the main
goroutine block and Greet
(in step 4.
) Can additional values be written to a channel? An explanation would really clear up my confusion :)
cheers, D.D.
Thanks for your reply, I've created a simpler program to demonstrate. concurrent
package main import ( "fmt" ) func do2(c chan int) { fmt.Println(<-c) } func do(c chan int) { // 4. this statement is trying to write another value "2" to the channel // Channel already contains "1" as the value which has not been read yet. // this statement will wait for "1" to get read and block the execution. // Scheduler will look for other goroutines that can execute. // However, this("do") is blocked as well as "main" is blocked too and // there are no other goroutines to execute. // Hence, will result in a "Deadlock" fatal error. c <- 2 fmt.Println(<-c) } func main() { // 1. Declare a channel c := make(chan int) // 2. Declare "do" goroutine go do(c) // 3. write "1" to the channel // This will block and wait for program's other goroutines to read the value. // however, there is only "do" goroutine is defined can run at this point. // Scheduler, will try to run "do" goroutine. c <- 1 go do2(c) }
The deadlock
can be fixed by exchanging the c <- 1
and go do2(c)
statements.
In Go, when you send a value on a channel (c <- "main::Hello"
in step 3), send The Goroutine will block until another Goroutine is ready to receive a value from the channel. However, this does not mean that no other goroutines can continue executing. In your code, both the Greet
and Greet2
coroutines are waiting for the value from the channel, so when you send the value in step 3, one of them (no guarantee which one) ) will unblock and continue execution.
Let me break down the sequence of events step by step:
c
. Greet
and Greet2
, and both are waiting for a value from the channel. Greet
or Greet2
) is not blocked from receiving the value. Greet
Unblock and continue execution. It logs the message "4. Add 'Greet::John' to channel..." and sends "Greet::John!" on the channel. This blocks Greet
again because no other goroutine can read from the channel at this time. Greet2
Unblock and continue execution. It logs the message "5. Read Greet2::main::Hello" and reads the value "main::Hello" from the channel. Greet
is still blocked while writing to the channel, and Greet2
is blocked from not reading from the channel. Greet
is still blocked on write, it never logs "8. Read Greet::main::Bye !" So the key to understanding the behavior here is that when you send a value on a channel, it unlocks any goroutines that are waiting to read data from the channel. The order in which waiting goroutines are unblocked is undefined and depends on the scheduler. In your case, Greet2
happens to be unlocked first, but it could also be Greet
.
In summary, the behavior you observe is entirely consistent with how Go channels work, with the caveat that execution order among competing Goroutines is not guaranteed.
The above is the detailed content of Understanding golang blocking channel behavior by writing multiple times to the channel. For more information, please follow other related articles on the PHP Chinese website!