Search code examples
goconcurrency

fatal error: all goroutines are asleep - deadlock! error GO


I'm new to concurrency in Go so I tried an example with channels and goroutines. I want to have the producer-consumer pattern. The producer function forever gives random strings and the consumer modifies them by making them uppercase. I want to run it for a limited time(2 sec).

package main

import (
    "fmt"
    "math/rand"
    "strings"
    "time"
)

func producer(x []string, c chan string) {
    i := 1
    for i > 0 {
        randomIndex := rand.Intn(len(x))
        pick := x[randomIndex]
        c <- pick
    }
}

func consumer(x string, c chan string) {
    x1 := strings.ToUpper(x)
    c <- x1
}

func main() {

    s := []string{"one", "two", "three", "four"}
    c1 := make(chan string)
    d1 := make(chan string)
    go producer(s, c1)
    go consumer(<-c1, d1)

    stop := time.After(2000 * time.Millisecond)
    for {
        select {
        case <-stop:
            fmt.Println("STOP AFTER 2 SEC!")
            return
        default:
            fmt.Println(<-d1)
            time.Sleep(50 * time.Millisecond)
        }
    }
}

I'm getting only one array element and some errors. What changes need to be made so this example would work?

OUTPUT:

TWO

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]: main.main()

goroutine 6 [chan send]: main.producer({0xc00004e040, 0x4, 0x0?}, 0x0?) created by main. main exit status 2


Solution

  • Your consumer should run in a loop, this has been mentioned already.

    Change the first parameter of the consumer so that it is a chan string instead of a string. That way the producer can continuously write to this channel for the consumer to publish on another channel until the time limit expires.

    func consumer(consumeChan chan string, outCh chan string) {
        for {
            select {
            case s := <- consumeChan:
                s = strings.ToUpper(s)
                outCh <- s
            }
        }
    }
    

    Right now in the main func before go consumer() is called you are waiting for the first response to the c1 channel from the producer. Instead of doing that pass in the c1 channel as the first parameter.

    func main() {
        s := []string{"one", "two", "three", "four"}
        c1 := make(chan string)
        d1 := make(chan string)
        go producer(s, c1)
        go consumer(c1, d1)
    
        stop := time.After(2000 * time.Millisecond)
        for {
            select {
            case <-stop:
                fmt.Println("STOP AFTER 2 SEC!")
                return
            case response := <- d1:
                fmt.Println(response)
                time.Sleep(50 * time.Millisecond)
            }
        }
    }
    

    This should show you continuous writes of random numbers by the producer on the c1 channel, and continuous writes on the d1 channel by the consumer with the text in all uppercase until the 2 seconds is up.