Reputation: 13
This is plain producer consumer problem with golang channels. I want to put something in channel, and out to write it on console.
package main
import (
"fmt"
"sync"
"time"
)
func main() {
chIn := make(chan string)
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
for i := 0; i < 3; i++ {
chIn <- "in"
fmt.Println("in")
}
close(chIn)
}()
go func() {
defer wg.Done()
for range chIn {
<-chIn
fmt.Println("out")
}
}()
wg.Wait()
}
Upvotes: 0
Views: 1108
Reputation: 23036
Dealing with your questions in reverse order...
As Burak Serdar mentions in their comment, the difference in the number of outputs vs the number of inputs is caused by the fact that you have an additional, unnecessary read from the channel in the second goroutine:
This:
for range chIn {
<-chIn
fmt.Println("out")
}
Should be something more like:
for in := range chIn {
fmt.Println(in)
}
This will deal with the different number of outputs vs inputs.
To understand the problems with "sorting", it might help to send values that help identify the order in which they were sent, and to print those values so that you can see the order in which they are received.
So, for sending:
chIn <- fmt.Sprintf("#%d", i+1)
fmt.Printf("sent #%d\n", i+1)
The change suggested above to the second routine already takes care of outputting the received value but you may still be confused by the output. You might for example see a value (seemingly) being received before it has even been sent!
There are two reasons for this:
you set up the sending goroutine before establishing the receiving go routine
you emit a "sent" log after sending, which creates a window of opportunity for the message to be received and processed before the sending goroutine has had a chance to emit that log
The solution is to address these sequencing problems:
establish your receiver goroutine first - i.e. don't start sending messages to the channel until you have started the goroutine to start reading from it (this isn't always necessary when dealing with channels, but may help you understand what is going on in this case)
Emit a "sending" message to the console before sending a message to the channel; this eliminates the possibility of the sent message being received and processed before the logged output is emitted
This should result in something similar to this:
package main
import (
"fmt"
"sync"
)
func main() {
chIn := make(chan string)
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
for in := range chIn {
fmt.Printf("rcvd: %s\n", in)
}
}()
go func() {
defer wg.Done()
for i := 0; i < 3; i++ {
fmt.Printf("sending #%d\n", i+1)
chIn <- fmt.Sprintf("#%d", i+1)
}
close(chIn)
}()
wg.Wait()
}
The output will still consist of interleaved send and receive logs, but the sequencing should now be sensible, yielding something similar to:
sending #1
sending #2
rcvd: #1
rcvd: #2
sending #3
rcvd: #3
A runnable solution may be found in this Playground
Upvotes: 0