一个通道的范围结束于死锁。

huangapple go评论105阅读模式
英文:

Range a channel finishes with deadlock

问题

以下代码以致命错误结束:所有goroutine都处于休眠状态-死锁!

// 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
}

这是输出结果:

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!

我的理解是,range 会等待从通道接收数据,直到通道关闭,但是在 for 循环内部添加 close(ch)、在 func() 内部或之后添加 close(ch) 都会使情况变得更糟(fmt.Println 不会输出任何内容)- 还尝试了使用 defer(同样的问题)。

正确的方法是什么?在这种情况下,range 不是正确的解决方案吗?

非常感谢!

英文:

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 &quot;fmt&quot;

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 &lt;- Frequency(s)
		}()
	}
	m := FreqMap{}
	for c := range ch {
		fmt.Println(&quot;channel:&quot;, 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 !

答案1

得分: 10

Range只有在通道关闭时才会停止。你遇到了一个死锁问题,因为没有任何东西写入通道,但你却一直等待着有东西被写入。你可以添加一个sync.WaitGroup,并在所有写入通道的goroutine完成后关闭通道。

你还应该更改循环,因为存在传递错误值的可能性,因为你正在闭包中引用循环变量,而该变量可以并发更改;这样会更可靠:

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

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 &lt;- Frequency(s)
    }(s)
}

答案2

得分: 1

我更喜欢使用for...select来接收通道中的消息。

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
            }
        }
    }
}

请注意,我只翻译了代码部分,其他内容被忽略了。

英文:

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:=&lt;-out:
		    fmt.Printf(&quot;out : %+v\n&quot;, s)
	    default:
		    time.Sleep(50 * time.Millisecond)
		    if life--;life==0 {
			    return
		    }
	    }
    }
}

huangapple
  • 本文由 发表于 2017年7月11日 03:52:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/45020481.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定