I'm new to Golang and I have hard time figuring out why exactly the following code produces deadlock. Also, how can I fix it so it works?
package main
import "fmt"
func main() {
m := make(map[int]chan string)
go func() {
m[0] = make(chan string)
m[0] <- "abab"
}()
fmt.Println(<-m[0])
}
EDIT:
Thanks for your answers! Unfortunately, initializing m[0] with
m[0] = make(chan string)
before launching a new goroutine is not exactly what I want. My problem is: is there a way to create channels "dynamically"? E.g. I have a map m of type map[int]chan string and I receive requests that contain something like id of type int. I would like to send a message via channel map[id], but initializing channels for every int would be too costly. How do I solve/work around this?
So, in other words, I would like to have a separate job queue for every id and initialize each queue lazily.
Updated answer after OP updated the question
You can just loop on all the keys in your map, maybe have another goroutine that keeps looping on all the keys. Obviously if a key hasnt been initialized, then it wont come up in the for range loop. For each key, you can then start a goroutine that listens so it doesnt block, or you can use a buffered channels so they wont block up to the buffer limit. You can also preferably use a waitGroup, rather than the time.Sleep(), these are only for this trivial example.
package main
import (
"fmt"
"time"
)
func main() {
m := make(map[int]chan string)
go func() {
m[0] = make(chan string)
m[0] <- "abab"
}()
time.Sleep(time.Second * 1) //sleep so the above goroutine initializes the key 0 channel
for key := range m{ //loop on all non-nil keys
fmt.Println(key)
go func(k int){ // goroutine to listen on this channel
fmt.Println(<- m[k])
}(key)
}
time.Sleep(time.Second * 1) //sleep so u can see the effects of the channel recievers
}
Old answer
This is how the flow is. The main goroutine starts. The map is created. The main goroutine encounters another goroutine. It spawns said goroutine and goes on with its life. Then it meets this line, fmt.Println(<-m[0]), which is a problem, since the map is indeed initialized, but the channel in the map itself isnt initialized! By the time the main goroutine has reached fmt.Println(<-m[0]), the other goroutine hadn't yet initialized the channel! So its a simple fix, just initialize the channel before spawning the goroutine and you're good to go!
package main
import "fmt"
func main() {
m := make(map[int]chan string)
m[0] = make(chan string)
go func() {
m[0] <- "abab"
}()
fmt.Println(<-m[0])
}
Edit: Note that fmt.Println(<-m[0]) is blocking, which means that if in that other goroutine, you dont send on the channel, you will also go into a deadlock, since you are trying to recieve on the channel when no one is actually sending.
You need to synchronize the creation of a channel.
As it stands, your main thread arrives at <-m[0] while m[0] is still an uninitialized channel, and receiving on an uninitialized channel blocks forever.
Your go routine creates a new channel and places it in m[0], but the main go routine is already listening on the prior zero value. Sending on this new channel also blocks forever, as there is nothing reading from it, so all go routines block.
To fix this, move m[0] = make(chan string) above your go routine, so it happens synchronously.
Related
I'm following a quick intro to Go and one of the examples is:
package main
import (
"fmt"
"time"
)
func worker(done chan bool) {
fmt.Print("working...")
time.Sleep(time.Second)
fmt.Println("done")
done <- true
}
func main() {
done := make(chan bool, 1)
go worker(done)
<-done
}
I understand whats occuring but I guess I'm not grasping the sequence of events or the limitations?
A channel is created called done with a buffer size of 1.
The channel is passed into a function
After the timer is complete it adds a true boolean to the channel
I'm not sure what the final <-done is doing though
from: https://gobyexample.com/channel-synchronization
Receiver operator <- followed by channel name (done in this case) is used to wait for a value written to channel from worker goroutine. (i.e this read operation will be blocking. If you omit <-done, main goroutine will exit immediately even before worker's goroutine start and you won't be able to see results)
You can do whatever you want with <-done as value: assign it to another variable, pass it as a parameter to another function or just ignore it as in your case... etc.
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.
iam currently playing around with go routines, channels and sync.WaitGroup. I know waitgroup is used to wait for all go routines to complete based on weather wg.Done() has been called enough times to derement the value set in wg.Add().
i wrote a small bit of code to try to test this in golang playground. show below
var channel chan int
var wg sync.WaitGroup
func main() {
channel := make(chan int)
mynums := []int{1,2,3,4,5,6,7,8,9}
wg.Add(1)
go addStuff(mynums)
wg.Wait()
close(channel)
recieveStuff(channel)
}
func addStuff(mynums []int) {
for _, val := range mynums {
channel <- val
}
wg.Done()
}
func recieveStuff(channel chan int) {
for val := range channel{
fmt.Println(val)
}
}
Iam getting a deadlock error. iam trying to wait on the routing to return with wg.Wait()? then, close the channel. Afterwards, send the channel to the recievestuff method to output the values in the slice? but it doesnt work. Ive tried moving the close() method inside the go routine after the loop also as i thought i may of been trying to close on the wrong routine in main(). ive found this stuff relatively confusing so far coming from java and c#. Any help is appreciated.
The call to wg.Wait() wouldn't return until wg.Done() has been called once.
In addStuff(), you're writing values to a channel when there's no other goroutine to drain those values. Since the channel is unbuffered, the first call to channel <- val would block forever, resulting in a deadlock.
Moreover, the channel in addStuff() remains nil since you're creating a new variable binding inside main, instead of assigning to the package-level variable. Writing to a nil channel blocks forever:
channel := make(chan int) //doesn't affect the package-level variable
Here's a modified example that runs to completion by consuming all values from the channel:
https://play.golang.org/p/6gcyDWxov7
I am trying to understand why my code does not work, so I managed to recreate the problem in a simpler example.
I expected this code to output the string "broadcasted", but it does not output anything.
package main
import (
"fmt"
"time"
)
type hub struct {
handle chan []byte
broadcast chan []byte
}
func (h *hub) init() {
for {
select {
case m := <-h.handle:
handler(m)
case _ = <-h.broadcast:
fmt.Println("broadcasted")
}
}
}
var socketHub = hub{
handle: make(chan []byte),
broadcast: make(chan []byte),
}
func main() {
go socketHub.init()
m := []byte("this is the message")
socketHub.handle <- m
time.Sleep(time.Second * 2)
}
func handler(m []byte) {
// Do some stuff.
socketHub.broadcast <- m
}
Why does this not work?
Your broadcast channel is unbuffered. This means that:
You send the message to the handle channel: main goroutine blocks until...
In the goroutine, the select case gets the message...
And calls (in the goroutine) handler, which sends the message to the broadcast channel, blocking until...
And that's it: your child goroutine is blocking, waiting on itself to pick the message. Concurrently, your main goroutine sleeps, then reaches the end of main, exits and terminates the program.
You can 'solve' it in several ways:
Make your broadcast channel buffered: this way, goroutine sends the message, which succeeds immediately, and returns in the for loop, selecting it back and printing as intended
Make your send a (new) goroutine, either in handler, or by calling go handler(m) in your loop
Have two different goroutines listen to handler and broadcast
Which one you choose depends on the exact problem you try to solve: in this tiny example, it's hard to find a 'best' one.
I'm trying to implement an Observer Pattern suggested here; Observer pattern in Go language
(the code listed above doesn't compile and is incomplete). Here, is a complete code that compiles but I get deadlock error.
package main
import (
"fmt"
)
type Publisher struct{
listeners []chan int
}
type Subscriber struct{
Channel chan int
Name string
}
func (p *Publisher) Sub(c chan int){
p.listeners = append(p.listeners, c)
}
func (p *Publisher) Pub(m int, quit chan int){
for _, c := range p.listeners{
c <- m
}
quit <- 0
}
func (s *Subscriber) ListenOnChannel(){
data := <-s.Channel
fmt.Printf("Name: %v; Data: %v\n", s.Name, data)
}
func main() {
quit := make(chan int)
p := &Publisher{}
subscribers := []*Subscriber{&Subscriber{Channel: make(chan int), Name: "1"}, &Subscriber{Channel: make(chan int), Name: "2"}, &Subscriber{Channel: make(chan int), Name: "3"}}
for _, v := range subscribers{
p.Sub(v.Channel)
go v.ListenOnChannel()
}
p.Pub(2, quit)
<-quit
}
Also, if I get rid of 'quit' completely, I get no error but it only prints first record.
The problem is that you're sending to quit on the same goroutine that's receiving from quit.
quit has a buffer size of 0, which means that in order to proceed there has to be a sender on one side and a receiver on the other at the same time. You're sending, but no one's on the other end, so you wait forever. In this particular case the Go runtime is able to detect the problem and panic.
The reason only the first value is printed when you remove quit is that your main goroutine is exiting before your remaining two are able to print.
Do not just increase channel buffer sizes to get rid of problems like this. It can help (although in this case it doesn't), but it only covers up the problem and doesn't truly fix the underlying cause. Increasing a channel's buffer size is strictly an optimization. In fact, it's usually better to develop with no buffer because it makes concurrency problems more obvious.
There are two ways to fix the problem:
Keep quit, but send 0 on it in each goroutine inside ListenOnChannel. In main, make sure you receive a value from each goroutine before moving on. (In this case, you'll wait for three values.)
Use a WaitGroup. There's a good example of how it works in the documentation.
In general this looks good, but there is one problem. Remember that channels are either buffered or unbuffered (synchronous or asynchronous). When you send to an unbuffered channel or to a channel with a full buffer the sender will block until the data has been removed from the channel by a receiver.
So with that, I'll ask a question or two of my own:
Is the quit channel synchronous or asynchronous?
What happens in Pub when execution hits quit<-0?
One solution that fixes your problem and allows the code to run is to change the second-to-last code line to be go p.Pub(2, quit). But there is another solution. Can you see what it is?
I don't actually get the same behavior you do if I remove <-quit from the original code. And this should not affect the output because as it is written that line is never executed.