I have this piece of Go code. I need to have this ability: write into channels in one place, and read them out in another place(or vice versa):
package main
import "fmt"
var ch1=make(chan int)
var ch2=make(chan int)
func f1() {
select {
case <- ch1:fmt.Println("ch1")
default: fmt.Println("default")
}
}
func f2() {
select {
case <- ch2:fmt.Println("ch2")
default: fmt.Println("default")
}
}
func main() {
go f1()
go f2()
ch1<-1
ch2<-2
}
It always prints sth like this:
default
ch1
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.main()
/tmp/sandbox970110849/prog.go:22 +0xa0
Further more I tried this:
package main
import (
"fmt"
"sync"
)
var ch1=make(chan int)
var ch2=make(chan int)
func f1() {
select {
case <- ch1:fmt.Println("ch1")
default: fmt.Println("default")
}
}
func f2() {
select {
case <- ch2:fmt.Println("ch2")
default: fmt.Println("default")
}
}
func w1() {
ch1 <-1
}
func w2() {
ch2 <-1
}
func main() {
var wg sync.WaitGroup
wg.Add(4)
go f1()
go f2()
go w1()
go w2()
wg.Wait()
}
Even more errors this time:
default
ch2
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [semacquire]:
sync.runtime_Semacquire(0x40e028, 0x0)
/usr/local/go/src/runtime/sema.go:56 +0x40
sync.(*WaitGroup).Wait(0x40e020, 0x14b720)
/usr/local/go/src/sync/waitgroup.go:130 +0x60
main.main()
/tmp/sandbox916639182/prog.go:36 +0x100
goroutine 8 [chan send]:
main.w1()
/tmp/sandbox916639182/prog.go:23 +0x40
created by main.main
/tmp/sandbox916639182/prog.go:34 +0xc0
Where did I go wrong and how to fix it?
Your main() function tries to send on all channels, and there is only a one-time attempt to read from those channels in separate, concurrent goroutines. It's up to the goroutine scheduler if this will succeed. If the non-blocking receive in f2() is scheduled earlier than the send in main(), then the later send in main() will block forever (no one will attempt to receive from ch2 again).
One way to get rid of the deadlock is to use receive operation instead of the non-blocking receive (try it on the Go Playground):
func f1() {
<-ch1
fmt.Println("ch1")
}
func f2() {
<-ch2
fmt.Println("ch2")
}
So no matter when main() gets to the point to send values on these channels, there will always be a receiver ready to proceed, so the main() won't get stuck.
Note that when main() returns, the app ends, it does not wait for non-main goroutines to finish. So you might not see ch1 and ch2 printed on your console. For details, see No output from goroutine in Go.
If your intent is to wait for all goroutines to finish their work before your app exists, use sync.WaitGroup for that (try it on the Go Playground):
var ch1 = make(chan int)
var ch2 = make(chan int)
var wg sync.WaitGroup
func f1() {
defer wg.Done()
<-ch1
fmt.Println("ch1")
}
func f2() {
defer wg.Done()
<-ch2
fmt.Println("ch2")
}
func main() {
wg.Add(1)
go f1()
wg.Add(1)
go f2()
ch1 <- 1
ch2 <- 2
wg.Wait()
}
See more examples here: Solving goroutines deadlock; and Prevent the main() function from terminating before goroutines finish in Golang.
Another option is to give a buffer of 1 to the channels, so the main() can send 1 value on them without a receiver ready to receive from the channels (try this one on the Go Playground):
var ch1 = make(chan int, 1)
var ch2 = make(chan int, 1)
With this the main() can proceed without f1() and f2(), so again, there is no guarantee you will see anything printed.
Channels used here are unbuffered channels.
Removed deadlock from first example, Refer the link:https://play.golang.org/p/6RuQQwC9JkA
However,if main routine exits all the associated goroutines will be killed automatically. That's why we can see that value is printed arbitrarily.
Removed deadlock from Second example, Refer the link:https://play.golang.org/p/yUmc_jjZMgV
sync.WaitGroup is making main goroutine wait that's why we can observe the output everytime our program executes.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With