user3591466
user3591466

Reputation: 867

Deadlock when spawning goroutine in a for loop

Consider the following go playground

package main

import "fmt"

func main() {

    var chan_array [2]chan int

    chan1 := make(chan int)
    chan2 := make(chan int)

    chan_array[0] = chan1
    chan_array[1] = chan2


    for i := 0; i < 2; i++ {
        go func() {

            select {
                case x := <- chan_array[i]:
                    if (x == 0) {
                        return
                    }       
                    fmt.Println(x)
            }
        }()
    }

    chan1<- 1
    chan2<- 2
    chan1<- 0
    chan2<- 0
}

The code above is trying to create 2 running goroutines with that listens to the channel to signal print or close.

But the above code run into dead lock.

I am not exactly sure why

Can someone point out my mistake?

Thanks

Upvotes: 2

Views: 291

Answers (2)

Zan Lynx
Zan Lynx

Reputation: 54325

By the time the goroutines are running the variable i has already incremented. Pass it as a function parameter instead.

In fact, never rely on variables from a function closure in goroutines. It's too unreliable.

Upvotes: 1

user6169399
user6169399

Reputation:

There are some problems:
What is the value of i when chan_array[i-1] runs:

for i := 0; i < 2; i++ {
    go func() {
        select {
        case x := <- chan_array[i-1]:
            if x == 0 {
                return
            }
            fmt.Println(x)
        }
    }()
}

try this:

for i := 0; i < 2; i++ {
    go func(i int) { 
        select {
        case x := <-chan_array[i]:
            if x == 0 {
                return
            }
            fmt.Println(x)
        }
    }(i)
}

Let's simplify your code (with some corrections):

package main

import "fmt"

func main() {
    chan1 := make(chan int)
    chan2 := make(chan int)

    go routine(chan1)
    go routine(chan2)

    chan1 <- 1
    chan2 <- 2
    chan1 <- 0
    chan2 <- 0
}

func routine(ch chan int) {
    select {
    case x := <-ch:
        if x == 0 {
            return
        }
        fmt.Println(x)
    }
}

With these:

chan1 <- 1
chan2 <- 2

fatal error:

all goroutines are asleep - deadlock!

your goroutines finished and no goroutines listening to chan1 and chan1 here:

chan1 <- 0
chan2 <- 0

Your corrected working sample code is:

package main

import "fmt"

func main() {
    chan1 := make(chan int)
    chan2 := make(chan int)

    go routine(chan1)
    go routine(chan2)

    chan1 <- 1
    chan2 <- 2
    chan1 <- 0
    chan2 <- 0
}

func routine(ch chan int) {
    for {
        select {
        case x := <-ch:
            if x == 0 {
                return
            }
            fmt.Println(x)
        }
    }
}

output:

1
2

Upvotes: 4

Related Questions