Trying to understand go func with WaitGroup - go

I have the following code https://play.golang.org/p/9jPlypO4d-
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var wg sync.WaitGroup
wg.Add(1)
c := make(chan int)
go func() {
defer wg.Done()
for {
if <-c == -1 {
fmt.Print(":")
return
}
fmt.Print(".")
time.Sleep(time.Second)
}
}()
c <- 0
time.Sleep(time.Second * 5)
c <- -1
wg.Wait()
}
I wonder why there is only one . printed? Shouldn't it be like 4 or 5?

if <-c == -1 will block until there is something in the channel. So, the first value is 0, it gets it, prints out a ., sleeps one second (while outside the goroutine it is sleeping 5 seconds), then it blocks until it gets the next value. And then it returns.
The only way to read a channel without blocking (that I know of) is to use a select statement with a default case.
go func() {
defer wg.Done()
for {
select {
case x, ok := <-c:
if ok && x == -1 {
fmt.Print(":")
return
}
default:
fmt.Print(".")
}
time.Sleep(time.Second)
}
}()
https://play.golang.org/p/nOG_hfih4D

Related

How to execute multiple goroutines in parallel without a Deadlock

So I have been trying to run mutliple goroutines in parallel using a WaitGroup. Whatever I try I always end up with a "fatal error: all goroutines are asleep - deadlock!"
This is what my code looks like right now:
package main
import (
"fmt"
"sync"
"time"
)
func main() {
ch := make(chan time.Duration)
var wg sync.WaitGroup
for _, v := range []time.Duration{5, 1} {
wg.Add(1)
go func() {
defer wg.Done()
wait(v, ch)
}()
}
wg.Wait()
}
func wait(seconds time.Duration, c chan time.Duration) {
time.Sleep(seconds * time.Second)
c <- seconds
}
However this results in a deadlock and I can't figure out why.
I have been trying to read the values after the WaitGroup with the following code:
close(ch)
for v := range ch {
fmt.Println(v)
}
However it seems it wouldn't even reach this part.
Thank you!
There are two problems withe the code:
You must close the channel only after all go-routines have finished
The value of v is being lost during the iteration
In go, the for loop will reuse v, so all go-routines will have the same value on the wait call.
package main
import (
"fmt"
"sync"
"time"
)
func main() {
ch := make(chan time.Duration)
var wg sync.WaitGroup
for _, v := range []time.Duration{5, 1} {
wg.Add(1)
v := v // <- this
go func() {
defer wg.Done()
wait(v, ch)
}()
}
go func() { // Close the channel only after go-routines finish
wg.Wait()
close(ch)
}()
for v := range ch { // Will loop until channel is closed
fmt.Println(v)
}
}
func wait(seconds time.Duration, c chan time.Duration) {
time.Sleep(seconds * time.Second)
c <- seconds
}
You need to close the worker channel after all the workers are done. You are closing it immediately from the main goroutine.
So do this:
go func() {
wg.Wait()
close(ch)
}()
Also your wait function already takes a time.Duration so really it should be in the real time at that point & not multiplied by time.Second. if you want to pass in units of seconds, consider changing the input type to int to avoid confusion.

How to fix the deadlock in this code, and extend it so that it can wait on a channel which has no writers?

I'm trying to learn channels in Go, so this is a contrived example of having a channel where there are multiple writers, but only one reader. This is a very basic example, but I would like to take it further, by imagining its a http server, where there is a goroutine created for each new request, and each goroutine makes a write on a channel.
func Start2(){
var wg sync.WaitGroup
wg.Add(2)
c := make(chan int)
go func(){
defer wg.Done()
i := 0
for {
i+=2
c<-i
time.Sleep(time.Duration(rand.Intn(5)) * time.Second)
if i > 10 {
break
}
}
}()
go func(){
defer wg.Done()
i := 1
for {
i+=2
c<-i
time.Sleep(time.Duration(rand.Intn(5)) * time.Second)
if i > 10 {
break
}
}
}()
for a := range c {
log.Println(a)
}
wg.Wait()
}
Why does this deadlock?
How could I organise the logic of reading from the channel, so that the code doesn't deadlock when there are no writers on the channel? (For instance, in the end goal example, if there isn't currently a request being made to the http server)?
Unbuffered channel needs at least two goroutines to operate and by exiting one of then. the Go runtime is smart enough to detect the deadlock, so you have two options here:
Since you want to use the channel in the http server - there is no deadlock here:
package main
import (
"fmt"
"log"
"net/http"
"time"
)
func main() {
http.HandleFunc("/", home)
go func() {
err := http.ListenAndServe(":8080", nil)
if err != nil {
log.Fatal(err)
}
}()
count := 0
for a := range c {
count++
fmt.Println(count, a)
}
}
var c = make(chan time.Time)
func home(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hi")
c <- time.Now()
}
Or close the channel to force the main goroutine to exit too - just for your current example code - try it:
package main
import (
"log"
"math/rand"
"sync"
"time"
)
func main() {
wg := &sync.WaitGroup{}
wg.Add(2)
c := make(chan int)
go routine(0, c, wg)
go routine(1, c, wg)
go func() {
wg.Wait()
close(c)
}()
for a := range c {
log.Println(a)
}
}
func routine(i int, c chan int, wg *sync.WaitGroup) {
defer wg.Done()
for {
i += 2
c <- i
time.Sleep(time.Duration(rand.Intn(5)) * time.Second)
if i > 10 {
break
}
}
}
You are not closing your channel, you need to close it when you are done.
You can create a goroutine for reading from your channel and close it after the wait group.
Example:
var wg sync.WaitGroup
wg.Add(2)
c := make(chan int)
go func() {
defer wg.Done()
i := 0
for {
i += 2
c <- i
time.Sleep(time.Duration(rand.Intn(5)) * time.Second)
if i > 10 {
return
}
}
}()
go func() {
defer wg.Done()
i := 1
for {
i += 2
c <- i
time.Sleep(time.Duration(rand.Intn(5)) * time.Second)
if i > 10 {
break
}
}
}()
//read channel in a goroutine
go func() {
for a := range c {
log.Println(a)
}
}()
wg.Wait()
//close channel when done
close(c)

Dead lock in goroutines

can someone give me some insight about this code, Why this get deadlock error in for x:=range c
func main() {
c:=make(chan int,10)
for i:=0;i<5;i++{
go func(chanel chan int,i int){
println("i",i)
chanel <- 1
}(c,i)
}
for x:=range c {
println(x)
}
println("Done!")
}
Because this:
for x:=range c {
println(x)
}
will loop until the channel c closes, which is never done here.
Here is one way you can fix it, using a WaitGroup:
package main
import "sync"
func main() {
var wg sync.WaitGroup
c := make(chan int, 10)
for i := 0; i < 5; i++ {
wg.Add(1)
go func(chanel chan int, i int) {
defer wg.Done()
println("i", i)
chanel <- 1
}(c, i)
}
go func() {
wg.Wait()
close(c)
}()
for x := range c {
println(x)
}
println("Done!")
}
Try it on Go Playground
You create five goroutines, each sending an integer value to the channel. Once all those five values are written, there's no other goroutine left that writes to the channel.
The main goroutine reads those five values from the channel. But there are no goroutines that can possibly write the sixth value or close the channel. So, you're deadlocked waiting data from the channel.
Close the channel once all the writes are completed. It should be an interesting exercise to figure out how you can do that with this code.
Channel needs to be closed to indicate task is complete.
Coordinate with a sync.WaitGroup:
c := make(chan int, 10)
var wg sync.WaitGroup // here
for i := 0; i < 5; i++ {
wg.Add(1) // here
go func(chanel chan int, i int) {
defer wg.Done()
println("i", i)
chanel <- 1
}(c, i)
}
go func() {
wg.Wait() // and here
close(c)
}()
for x := range c {
println(x)
}
println("Done!")
https://play.golang.org/p/VWcBC2YGLvM

Go: transformed channel

Let's say I have an int channel in Go:
theint := make(chan int)
I want to wrap this channel in a new channel called incremented
incremented := make(chan int)
Such that:
go func() { theint <- 1 }
<- incremented // 2
appended can be assumed to be the only one that reads from the int.
It will work if a run a goroutine in the background
go func() {
for num := range theint {
incremented <- num + 1
}
}
However, I prefer to do it without a goroutine since I can't control it in my context.
Is there a simpler way to do it?
One thing that came to mind is python's yield:
for num in theint:
yield num + 1
Is something like this possible in go?
Generator pattern
What you are trying to implement is generator pattern. To use channels and goroutines for implementation of this pattern is totally common practice.
However, I prefer to do it without a goroutine since I can't control it in my context.
I believe the problem is deadlock
fatal error: all goroutines are asleep - deadlock!
To avoid deadlocks and orphaned (not closed) channels use sync.WaitGroup. This is an idiomatic way to control goroutines.
Playground
package main
import (
"fmt"
"sync"
)
func incGenerator(n []int) chan int {
ch := make(chan int)
var wg sync.WaitGroup
wg.Add(len(n))
for _, i := range n {
incremented := i + 1
go func() {
wg.Done()
ch <- incremented
}()
}
go func() {
wg.Wait()
close(ch)
}()
return ch
}
func main() {
n := []int{1, 2, 3, 4, 5}
for x := range incGenerator(n) {
fmt.Println(x)
}
}
One thing you can also consider is having a select on the int channel and an exit channel - in an infinite for loop. You can choose a variable increment value too. Please see code below:
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var accum int //accumulator of incremented values
var wg sync.WaitGroup
c1 := make(chan int)
exChan := make(chan bool)
wg.Add(1)
go func() {
time.Sleep(time.Second * 1)
c1 <- 1
wg.Done()
}()
wg.Add(1)
go func() {
time.Sleep(time.Second * 2)
c1 <- 2
wg.Done()
}()
wg.Add(1)
go func() {
time.Sleep(time.Second * 2)
c1 <- 5
wg.Done()
}()
go func() {
wg.Wait()
close(exChan)
}()
for {
var done bool
select {
case incBy := <-c1: //Increment by value in channel
accum += incBy
fmt.Println("Received value to increment:", incBy, "; Accumulated value is", accum)
case d := <-exChan:
done = !(d)
}
if done == true {
break
}
}
fmt.Println("Final accumulated value is", accum)
}
Playground: https://play.golang.org/p/HmdRmMCN7U
Exit channel not needed, if we are having non-zero increments always. I like #I159 's approach too!
Anyways, hope this helps.

Golang: How can I stop the execution of a for loop from outside the loop?

I am using an infinite for loop with a label. Outside the scope of the for loop, I have a scheduled function running as a go routine. I want to break the for loop from the scheduled function when a certain condition is met. How can I accomplish the same ? This is what I am trying, which obviously won't work due to scope issue.
package main
import (
"fmt"
"time"
"sync"
)
func main() {
count := 0
var wg sync.WaitGroup
wg.Add(1)
t := time.NewTicker(time.Second*1)
go func (){
for {
fmt.Println("I will print every second", count)
count++
if count > 5 {
break myLoop;
wg.Done()
}
<-t.C
}
}()
i := 1
myLoop:
for {
fmt.Println("iteration", i)
i++
}
wg.Wait()
fmt.Println("I will execute at the end")
}
Make a signalling channel.
quit := make(chan struct{}{})
Close it when you want to break a loop.
go func (){
for {
fmt.Println("I will print every second", count)
count++
if count > 5 {
close(quit)
wg.Done()
return
}
<-t.C
}
}()
Read on closed channel returns immediately with zero value (but we dont need it in this case). Otherwise reading from it blocks and select pass execution to "default" case.
myLoop:
for {
select {
case <- quit:
break myLoop
default:
fmt.Println("iteration", i)
i++
}
}
Darigaaz's answer works for a single goroutine, but closing a closed channel panics (and you also don't need a waitgroup in that instance). If you have multiple goroutines, and want the loop to exit after all of them have finished, use a waitgroup with a closer routine:
https://play.golang.org/p/RhmUzWhneT
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var wg sync.WaitGroup
quitCh := make(chan struct{})
for i := 1; i <= 5; i++ {
wg.Add(1)
go func(i int) {
count := 1
t := time.NewTicker(time.Millisecond)
for count <= 5 {
fmt.Printf("Goroutine %v iteration %v\n", i, count)
count++
<-t.C
}
wg.Done()
}(i)
}
// This is the closer routine.
go func() {
wg.Wait()
close(quitCh)
}()
t := time.NewTicker(500 * time.Microsecond)
loop:
for i := 1; ; i++ { // this is still infinite
select {
case <-quitCh:
break loop // has to be named, because "break" applies to the select otherwise
case <-t.C:
fmt.Println("Main iteration", i)
}
}
fmt.Println("End!")
}
As an alternative to the named loop style, you can instead use a fallthrough break in that select:
for i := 1; ; i++ { // this is still infinite
select {
case <-quitCh:
// fallthrough
case <-t.C:
fmt.Println("Main iteration", i)
continue
}
break // only reached if the quitCh case happens
}

Resources