1
votes

I'm learning Go. I'm trying to solve the deadlock issue on goroutines using semaphores. I have created two functions that write to an unbuffered channel. the reading is happening on the main channel. the third function is supposed to close the channel. when I run the program, it throws this error fatal error: all goroutines are asleep - deadlock! can somebody explain to me why this isn't working.

import (
    "fmt"
    "sync"
)

var wg sync.WaitGroup
var s = []string{"a", "b", "c", "d"}
var w = []string{"w", "x", "t", "z", "p"}

func f(c chan string, ch chan bool) {
    for _, word := range s {
        c <- word
    }
    fmt.Println("f about to exit")
    ch <- true
    wg.Done()
}
func g(c chan string, ch chan bool) {
    for _, word := range w {
        c <- word
    }
    fmt.Println("g about to exit")
    ch <- true
    wg.Done()
}
func f1(ch chan string, c chan bool) {
    <-c
    <-c
    fmt.Println("about to close channel")
    close(ch)
}

func main() {
    ch := make(chan string)
    c := make(chan bool)
    wg.Add(3)
    go f(ch, c)
    go g(ch, c)
    go f1(ch, c)
    for word := range ch {
        fmt.Println(word)
    }

    wg.Wait()
}
2

2 Answers

1
votes

You should be consistent in naming the data channel and signalling channel.
That's all.

You have two producer go routines and one consumer go routine here,
so let me simplify your code first, try it online:

func main() {
    ch := make(chan string)
    done := make(chan struct{})
    go func() {
        for _, word := range []string{"1", "2", "3"} {
            ch <- word
        }
        done <- struct{}{}
    }()
    go func() {
        for _, word := range []string{"10", "20", "30", "40"} {
            ch <- word
        }
        done <- struct{}{}
    }()
    go func() {
        <-done
        <-done
        close(ch)
    }()
    for word := range ch {
        fmt.Print(word, " ")
    }
    fmt.Println()
}

Output:

1 2 10 3 20 30 40 

Notes:

  1. You don't need sync.WaitGroup since done channel is enough for signalling.
  2. Empty struct is good enough for signalling.
  3. Upon receiving two done signals, you can close the data channel, so the main exits from the loop.
0
votes

After starting three goroutines, you read from the channel ch until ch is closed. All functions get ch as the first argument that they write to, but f1 is writing to its second argument, not to the first one. That is, f1 is writing to channel c in main, so f1 is blocked at the first write, because nobody is reading from c. Main goroutine is blocked reading from ch because after f and g are done, there is nobody writing to it.

This looks like a channel naming confusion: two functions get (c, ch), one gets (ch, c), but all are called with (ch, c).