Why channel with no buffer cause this bug? [duplicate] - go

This question already has answers here:
Buffered/Unbuffered channel
(3 answers)
fatal error: all goroutines are asleep - deadlock! when channel is not buffered
(1 answer)
Why is the order of channels receiving causing/resolving a deadlock in Golang?
(3 answers)
In the Go select construct, can I have send and receive to unbuffered channel in two case statements?
(2 answers)
Closed 7 days ago.
package main
import (
"fmt"
"sync"
)
func main() {
ch := make(chan struct{})
wg := &sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
ch <- struct{}{}
}()
wg.Wait()
<-ch
fmt.Println("finished")
}
result
fnished will not be printed
package main
import (
"fmt"
"sync"
)
func main() {
ch := make(chan struct{}, 1)
wg := &sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
ch <- struct{}{}
}()
wg.Wait()
<-ch
fmt.Println("finished")
}
result
finished can be printed immediately
The only difference between the two code is that the first code use no buffer channel, but the second code use channel with a buffer.
In the first code, ch <- struct{}{} can not be run. message can not be sent to the channel.
Why does that happen? Is there any official explanation or material?

In the first example wg.Done() can only be called after a value on the channel has been sent. But since the channel is unbuffered, the send can only happen if there's a ready receiver. The main goroutine would be the receiver, but it only receives after wg.Wait(). But wg.Wait() blocks until wg.Done() is called, so this will never happen, it's a deadlock.
This is in Spec: Send statements:
Communication blocks until the send can proceed. A send on an unbuffered channel can proceed if a receiver is ready.

When using non-buffered channels, sending or receiving commands from the channel are blocking.
This means with non-buffered channel, your go routine that sends the message is not done, hence it won't call wg.Done() which will keep the wg.Wait() method in blocking state.
If you run your code on the play server https://go.dev/play/p/oDpyL6-dARP, it will panic with an informative message: fatal error: all goroutines are asleep - deadlock!
When your using buffered channels, the go routine runs because the channel has a buffer, then wg.Done() is called, which release wg.Wait() afterwards.

Related

Why is WaitGroup.Wait() hanging when using it with go test? [duplicate]

This question already has answers here:
golang sync.WaitGroup never completes
(3 answers)
Best way of using sync.WaitGroup with external function
(2 answers)
GO - Pointer or Variable in WaitGroups reference
(1 answer)
Golang WaitGroup.Done() being skipped
(1 answer)
Closed 11 months ago.
Here's a simple example of what I mean
package main
import (
"sync"
"testing"
"time"
)
func TestWaitGroup(t *testing.T) {
var wg sync.WaitGroup
quitSig := make(chan struct{})
go func(wg sync.WaitGroup, quitChan, chan struct{}) {
defer func() {
t.Log("Done...")
wg.Done()
t.Log("Done!")
}()
t.Log("waiting for quit channel signal...")
<-quitChan
t.Log("signal received")
}(wg, quitSig)
time.Sleep(5*time.Second)
t.Log("Done sleeping")
close(quitSig)
t.Log("closed quit signal channel")
wg.Wait()
t.Log("goroutine shutdown")
}
When I run this, I get the following
=== RUN TestWaitGroup
main.go:18: waiting for quit channel signal...
main.go:23: Done sleeping
main.go:25: closed quit signal channel
main.go:20: signal received
main.go:14: Done...
main.go:16: Done!
Where it just hangs until it timesout. If you just do defer wg.Done() the same behaviour is observed. I'm running go1.18. Is this a bug or am I using not using WaitGroups properly in this context?
Two issues:
don't copy sync.WaitGroup: from the docs:
A WaitGroup must not be copied after first use.
you need a wg.Add(1) before launching your work - to pair with the wg.Done()
wg.Add(1) // <- add this
go func (wg *sync.WaitGroup ...) { // <- pointer
}(&wg, quitSig) // <- pointer to avoid WaitGroup copy
https://go.dev/play/p/UmeI3TdGvhg
You are passing a copy of the waitgroup, so the goroutine does not affect the waitgroup declared in the outer scope. Fix it by:
go func(wg *sync.WaitGroup, quitChan, chan struct{}) {
...
}(&wg, quitSig)

Go range over channel deadlock problems, should I close the channel?

package main
import (
"fmt"
"sync"
)
func main(){
ch1 := make(chan int,100)
ct := 0
var wg sync.WaitGroup
wg.Add(1)
go func(){
//defer close(ch1)
for i:= 0; i < 10;i ++{
ch1 <- i
}
}()
go func(){
defer wg.Done()
for x := range ch1{
fmt.Println(x)
}
}()
wg.Wait()
fmt.Println("numbers:",ct)
}
Why this code will return
fatal error: all goroutines are asleep - deadlock!
I've found if I closed the channel there will be no deadlock, but I don't know why it's like that.
Do I have to close the channel after I inputted all items into the channel?
for range over a channel only terminates if / when the channel is closed. If you don't close the channel and you don't send more values on it, the for range statement will block forever, and so will the main goroutine at wg.Wait().
The "sender" party should close the channel once it sent all values, signalling the "receiver" party that no more values will come on the channel.
So yes, you should close the channel:
go func() {
defer close(ch1)
for i := 0; i < 10; i++ {
ch1 <- i
}
}()
The for loop that reads from the channel will continue reading unless you close the channel. That is the reason for the deadlock, because the goroutine that reads from the channel is the only active goroutine, and there is no other goroutines that can write to it. When you close, the for loop terminates.
range unclosed ch1 will causing block;
even close ch1, you can still receive data from ch1;
It can be said, close(ch1) will send a special message to ch1, which can be used to notify the ch1 receiver that no more data will be received. So even if there is data in the ch1, you can close() without causing the receiver to not receive the remaining data.
The channel does not need to release resources through close. As long as there is no goroutine holding the channel, the related resources will be automatically released.
uncomment defer close(ch1) will solve this problems.
here

Wait for go routines to finish then read from channel

How do I wait for all go routines to finish and then read all the data from a channel?
Why is this example stuck waiting for the go routines to finish?
Go Playground
package main
import (
"fmt"
"sync"
"time"
)
func doStuff(i int, wg *sync.WaitGroup, messages chan<- string) {
defer wg.Done()
time.Sleep(time.Duration(i) * time.Second)
messages <- fmt.Sprintf("Doing stuff...%d", i)
}
func doMoreStuff(i int, wg *sync.WaitGroup, messages chan<- string) {
defer wg.Done()
time.Sleep(time.Duration(i) * time.Second)
messages <- fmt.Sprintf("Doing more stuff...%d", i)
}
func main() {
var wg sync.WaitGroup
var messages = make(chan string)
for start := time.Now();; {
elapsedTime := time.Since(start)
fmt.Println(elapsedTime)
if elapsedTime > time.Duration(3) * time.Second {
fmt.Println("BREAK")
break
}
for i := 0; i < 10; i++ {
wg.Add(1)
go doStuff(i, &wg, messages)
wg.Add(1)
go doMoreStuff(i, &wg, messages)
}
time.Sleep(time.Duration(1) * time.Second)
}
fmt.Println("WAITING")
wg.Wait()
fmt.Println("DONE")
for message := range messages {
fmt.Println(message)
}
}
If you want to wait for all goroutines to finish that send messages on a channel, and you want to start reading the messages after that, then you have no choice but to use a buffered channel that can "host" all the messages that can be sent on it by the goroutines.
This isn't something practical. And even if you'd go down this path, you would be able to receive and print the messages, but the for range loop doing so would never terminate because that only terminates when it receives all messages that were sent on the channel before it was closed, but you never close the channel. You may check this "half-working" solution on the Go Playground.
Instead launch a goroutine that waits for others to finish, and then close the channel:
go func() {
fmt.Println("WAITING")
wg.Wait()
close(messages)
}()
So now you may use for range to receive messages in the main goroutine:
for message := range messages {
fmt.Println(message)
}
fmt.Println("DONE")
Try this one on the Go Playground.
This solution is still not perfect: it first has to launch all goroutines, and only then it tries to receive values, and all the launched goroutines will be blocked on their send operation (until main is ready to receive those).
A better solution may be to launch a goroutine that receives the values, preferably before the goroutines that send messages on the channel (else they would be blocked on their sends anyway):
go func() {
for message := range messages {
fmt.Println(message)
}
}()
And wait for the goroutines and close the channel at the end of main():
fmt.Println("WAITING")
wg.Wait()
close(messages)
The problem with this is that when main() ends, so does your app, it does not wait for other non-main goroutines to finish. And this means it will not wait for the "consumer" goroutine to receive the messages.
To wait for that "consumer", you may use an additional sync.WaitGroup:
var wg2 sync.WaitGroup
wg2.Add(1)
go func() {
defer wg2.Done()
for message := range messages {
fmt.Println(message)
}
}()
// And the end of `main()`:
fmt.Println("WAITING")
wg.Wait()
close(messages)
fmt.Println("DONE")
wg2.Wait()
Try this one on the Go Playground.
The example is stuck because you are using an unbuffered channel. Both go-routines are blocked waiting to write to the channel since nothing is ready to read from it.
You can use a buffered channel but then you would just be using it as a data storage. Channels are more useful for communication. The question is why do you want to wait until all the writers are finished? In the general case of an unknown (unlimited) number of writers you would no know how big to make your channel.

Can't read from a channel in goroutine [duplicate]

This question already has answers here:
No output from goroutine
(3 answers)
Closed 3 years ago.
In this piece of code, a goroutine is created and tries to read from a buffered channel.
But the buffer is empty and should block the receiver, but it didn't. It will run but output nothin.
package main
import "fmt"
func fun(c chan int) {
fmt.Println(<-c)
}
func main() {
ch := make(chan int, 1)
//ch <- 1
go fun(ch)
}
First, the buffer is not empty, because you send a value on it:
ch <- 1
This send operation will not block because the channel is buffered.
Then you launch a goroutine, and the main() function ends. And with that, your program ends as well, it does not wait for other non-main goroutines to complete. For details, see No output from goroutine in Go.
Note that the same thing happens even if you comment out the send operation in main(): you launch a goroutine and then main() ends along with your app: it does not wait for the other goroutine.
To wait for other goroutines, often a sync.WaitGroup is used like this:
var wg = &sync.WaitGroup{}
func fun(c chan int) {
defer wg.Done()
fmt.Println(<-c)
}
func main() {
ch := make(chan int, 1)
ch <- 1
wg.Add(1)
go fun(ch)
wg.Wait()
}
Then output will be (try it on the Go Playground):
1
For details, see Prevent the main() function from terminating before goroutines finish in Golang

Deadlock issue when using routine with channel

I have issue when using Go routine with channel. The code looks like this:
func main() {
c := make(chan int)
var wg sync.WaitGroup
wg.Add(1)
go func (c chan int, x int) {
c <- x
fmt.Println(x)
close(c)
defer wg.Done()
}(c,10)
wg.Wait()
}
When run the code I got this error:
fatal error: all goroutines are asleep - deadlock!
I cannot understand why this issue happens. Please help me to understand
You have 2 goroutines in your example: the main goroutine running the main() function, and the other you launch inside it. The main goroutine waits for the other to complete (to call wg.Done()), and the other goroutine blocks in the line where it attempts to send a value on channel c. Since nobody is receiving from that channel, and because that channel is unbuffered, this goroutine will never advance, so all your 2 goroutines will block forever.
Note that defer wg.Done() should be the first statement in the goroutine. If it's the last, defer won't make any difference.
If the channel would have a buffer of at least one, the send operation could proceed:
c := make(chan int, 1)
And output will be (try it on the Go Playground):
10
If we leave the channel unbuffered, there must be another goroutine that receives from the channel, e.g.:
wg.Add(1)
go func() {
defer wg.Done()
x := <-c
fmt.Println("Received:", x)
}()
Then output will be (try it on the Go Playground):
10
Received: 10

Resources