【问题标题】:Range a channel finishes with deadlock范围通道以死锁结束
【发布时间】:2017-12-14 16:27:41
【问题描述】:

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

// 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 循环内或 func() 内或之后添加 close(ch) 会使事情变得更糟( fmt.Println 不会得到任何东西) - 也尝试过 defer(同样的问题)

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

非常感谢!

【问题讨论】:

    标签: go


    【解决方案1】:

    范围仅在通道关闭时停止。您遇到了死锁,因为没有任何内容写入通道,但您正在等待写入某些内容。您可以添加一个sync.WaitGroup 并在所有写入该通道的 goroutine 完成后关闭该通道。

    您可能还应该更改循环,因为可能会传递错误的值,因为您正在关闭可以同时更改的循环变量;这样会更可靠:

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

    【讨论】:

      【解决方案2】:

      我更喜欢使用 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
                  }
              }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-07-20
        • 2017-10-05
        • 2019-09-15
        • 2011-08-24
        • 1970-01-01
        • 1970-01-01
        • 2019-04-05
        • 2017-06-24
        相关资源
        最近更新 更多