【问题标题】:Always getting deadlock with channels总是与渠道陷入僵局
【发布时间】:2019-04-28 03:44:22
【问题描述】:

我正在学习使用 Go 频道,但我总是遇到死锁。这段代码可能有什么问题?数组大小不等时打印机随机停止工作;我想以某种方式通知打印机接收器停止工作会有所帮助。任何想法如何解决它?我的代码粘贴在下面。

package main

import (
    "fmt"
    "sync"
)

var wg = sync.WaitGroup{}
var wgs = sync.WaitGroup{}
var sg = make(chan int, 50)
var gp1 = make(chan int, 50)
var gp2 = make(chan int, 50)

func main(){
    wgs.Add(2)
    go Sender(0)
    go Sender(11)

    wg.Add(3)
    go Receiver()

    go Printer()
    go Printer2()

    wg.Wait()
}

func Sender(start int){
    defer wgs.Done()
    for i := start; i < 20; i++ {
        sg <- i
    }
}

func Receiver(){
    defer wg.Done()
    for i := 0; i < 20; i++{
        nr := <- sg
        if nr % 2 == 0{
            gp1 <- nr
        } else{
            gp2 <- nr
        }
    }
}

func Printer(){
    defer wg.Done()
    var m [10]int

    for i := 0; i < 10; i++ {
        m[i] = <- gp1
    }

    wgs.Wait()
    fmt.Println(m)
}

func Printer2(){
    defer wg.Done()
    var m [10]int

    for i := 0; i < 10; i++ {
        m[i] = <- gp2
    }

    wgs.Wait()

    fmt.Println(m)
}
// Better to use this one
// func Receiver(senderChannel <-chan int, printerChannel1 chan<- int, printerChannel2 chan<- int, wg *sync.WaitGroup) {

【问题讨论】:

    标签: go channels


    【解决方案1】:

    发件人生成(我认为是 28 条消息)。其中前 20 个中大约有一半用于 gp1 和 gp2 中的一个。 Printer 和 Printer2 然后卸载消息

    问题是,Receiver 拆分消息的方式取决于接收到的数字是奇数还是偶数。但你没有控制这一点。如果其中一台打印机的队列中的项目少于 10 个,它将挂起

    这是一个潜在的问题

    【讨论】:

      【解决方案2】:

      您的核心问题是这一切都是“航位推算”:他们希望看到固定数量的消息,但这并不一定与现实相符。您应该设置通道,以便在生成所有数据后关闭它们。

      这可能意味着设置一个中间函数来管理发送:

      func Sender(from, to int, c chan<- int) {
          for i := from; i < to; i++ {
              c <- i
          }
      }
      
      func SendEverything(c chan<- int) {
          var wg sync.WaitGroup
          wg.Add(2)
          go func() {
              defer wg.Done()
              Sender(0, 20, c)
          }()
          go func() {
              defer wg.Done()
              Sender(11, 20, c)
          }()
          wg.Wait()
          close(c)
      }
      

      使调度程序功能在频道中的所有内容上工作:

      func Receive(c <-chan int, odds, evens chan<- int) {
          for n := range c {
              if n%2 == 0 {
                  evens <- n
              } else {
                  odds <- n
              }
          }
          close(odds)
          close(evens)
      }
      

      然后你可以共享一个打印功能:

      func Printer(prefix string, c <-chan int) {
          for n := range c {
              fmt.Printf("%s: %d\n", prefix, n)
          }
      }
      

      最后,您有一个将所有内容拼接在一起的 main 函数:

      func main() {
          var wg sync.WaitGroup
          inputs := make(chan int)
          odds := make(chan int)
          evens := make(chan int)
      
          wg.Add(4)
          go func() {
              defer wg.Done()
              SendEverything(inputs)
          }()
          go func() {
              defer wg.Done()
              Receive(inputs, odds, evens)
          }()
          go func() {
              defer wg.Done()
              Printer("odd number", odds)
          }()
          go func() {
              defer wg.Done()
              Printer("even number", evens)
          }()
          wg.Wait()
      }
      

      完整的例子在https://play.golang.org/p/qTUqlt-uaWH

      请注意,我已经完全避免使用任何全局变量,并且任何事物都有一个希望不言自明的非常短的名称(in 是简单的整数,c 是一个通道)或者是完整的字(oddsevens)。我倾向于将 sync.WaitGroup 对象保留在创建它们的地方。由于所有内容都作为参数传递,因此我不需要同一函数的两个副本来作用于不同的全局变量,如果我选择为此编写测试代码,我可以创建自己的本地通道。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2022-07-14
        • 1970-01-01
        • 2021-05-04
        • 2017-10-06
        • 1970-01-01
        • 2020-04-07
        • 1970-01-01
        相关资源
        最近更新 更多