4
votes

The following code ends with a fatal error: all goroutines are asleep - deadlock!

// Package letter returns the frequency of letters in texts using parallel computation.
package letter

import "fmt"

const testVersion = 1

type FreqMap map[rune]int

func Frequency(s string) FreqMap {
    m := FreqMap{}
    for _, r := range s {
        m[r]++
    }
    return m
}

func ConcurrentFrequency(l []string) FreqMap {
    ch := make(chan FreqMap)
    for _, s := range l {
        go func() {
            ch <- Frequency(s)
        }()
    }
    m := FreqMap{}
    for c := range ch {
        fmt.Println("channel:", c)
        for k, v := range c {
            m[k] += v
        }
    }
    return m
}

This is output:

channel: map[121:8 101:42 98:8 104:28 102:6 71:1 59:1 97:33 110:15 103:15 112:6 109:5 116:38 10:7 87:2 107:1 108:17 99:3 117:7 39:5 79:4 114:27 105:15 44:7 119:8 100:12 63:2 65:1 118:3 45:1 32:72 111:18 115:24]
channel: map[97:33 110:15 111:18 117:7 108:17 45:1 115:24 10:7 79:4 32:72 121:8 100:12 105:15 63:2 107:1 71:1 119:8 114:27 103:15 102:6 65:1 44:7 87:2 118:3 101:42 98:8 116:38 39:5 112:6 104:28 109:5 99:3 59:1]
channel: map[99:3 116:38 104:28 108:17 44:7 117:7 119:8 114:27 10:7 87:2 110:15 100:12 103:15 107:1 32:72 111:18 102:6 59:1 45:1 101:42 109:5 63:2 115:24 97:33 105:15 112:6 65:1 71:1 79:4 121:8 98:8 39:5 118:3]
fatal error: all goroutines are asleep - deadlock!

My understanding is that range waits for data from channel until the channel is closed, but adding close(ch) inside the for loop, or inside the func() or after those, make things worse (the fmt.Println does not get anything) - tried also with defer (same problem)

What would be the correct approach? Isn't range the right solution in this case ?

Thanks a lot !

2

2 Answers

8
votes

Range only stops when the channel is closed. You're hitting a deadlock because nothing is writing to the channel, but you're sitting waiting for something to be written. You could add a sync.WaitGroup and close the channel after all of the goroutines writing to it have finished.

You should probably also change the loop as there is potential for the wrong values to be passed because you're closing over the loop variable which can change concurrently; this will be more reliable:

for _, s := range l {
    go func(s rune) {
        ch <- Frequency(s)
    }(s)
}
1
votes

I would prefer to use for...select to receive the message in channel.

func main() {

    in := State{[]What{A,B,C,D},[]What{},nil}
    out := make(chan State)
    go in.think(out)

    life:=5
    for {
        select {
        case s:=<-out:
            fmt.Printf("out : %+v\n", s)
        default:
            time.Sleep(50 * time.Millisecond)
            if life--;life==0 {
                return
            }
        }
    }
}