Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why all goroutines are asleep - deadlock. Identifying bottleneck

Tags:

go

channel

package main

import (
    "fmt"
    "runtime"
    "sync"
    "time"
)

func main() {
    intInputChan := make(chan int, 50)
    var wg sync.WaitGroup
    for i := 0; i < 3; i++ {
        wg.Add(1)
        go worker(intInputChan, wg)
    }
    for i := 1; i < 51; i++ {
        fmt.Printf("Inputs. %d \n", i)
        intInputChan <- i
    }
    close(intInputChan)
    wg.Wait()
    fmt.Println("Existing Main App... ")
    panic("---------------")
}

func worker(input chan int, wg sync.WaitGroup) {
    defer func() {
        fmt.Println("Executing defer..")
        wg.Done()
    }()

    for {
        select {
        case intVal, ok := <-input:
            time.Sleep(100 * time.Millisecond)
            if !ok {
                input = nil
                return
            }
            fmt.Printf("%d  %v\n", intVal, ok)

        default:
            runtime.Gosched()
        }
    }

}

error thrown is.

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [semacquire]: sync.(*WaitGroup).Wait(0xc082004600) c:/go/src/sync/waitgroup.go:132 +0x170 main.main() E:/Go/go_projects/go/src/Test.go:22 +0x21a

like image 203
VimleshS Avatar asked Dec 19 '22 06:12

VimleshS


2 Answers

I just tried it (playground) passing a wg *sync.WaitGroup and it works.

Passing sync.WaitGroup means passing a copy of the sync.WaitGroup (passing by value): the goroutine mentions Done() to a different sync.WaitGroup.

var wg sync.WaitGroup
for i := 0; i < 3; i++ {
    wg.Add(1)
    go worker(intInputChan, &wg)
}

Note the &wg: you are passing by value the pointer to the original sync.WaitGroup, for the goroutine to use.

like image 91
VonC Avatar answered Mar 03 '23 08:03

VonC


As mentioned, don't pass types from the sync package around by value, right near the top of the sync package documentation: "Values containing the types defined in this package should not be copied." That also includes the types themselves (sync.Mutex, sync.WaitGroup, etc).

However, several notes:

  • You can use just a single call to wg.Add if you know how many you're going to add (but as documented make sure it's done before anything can call Wait).
  • You don't want to call runtime.Gosched like that; it makes the workers busy loop.
  • You can use range to read from the channel to simplify stopping when it's closed.
  • For small functions you can use a closure and not bother to pass the channel or wait group at all.

That turns it into this:

package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    const numWorkers = 3

    c := make(chan int, 10)
    var wg sync.WaitGroup

    wg.Add(numWorkers)
    for i := 0; i < numWorkers; i++ {
        go func() {
            defer func() {
                fmt.Println("Executing defer…")
                wg.Done()
            }()

            for v := range c {
                fmt.Println("recv:", v)
                time.Sleep(100 * time.Millisecond)
            }
        }()
    }

    for i := 1; i < 51; i++ {
        fmt.Println("send:", i)
        c <- i
    }
    fmt.Println("closing…")
    close(c)

    fmt.Println("waiting…")
    wg.Wait()
    fmt.Println("Exiting Main App... ")
}

playground

like image 42
Dave C Avatar answered Mar 03 '23 08:03

Dave C