【问题标题】:all go routines are asleep - deadlock所有的 goroutine 都在休眠 - 死锁
【发布时间】:2014-02-22 00:59:06
【问题描述】:

我正在使用 Go 构建工作系统的骨架,但我收到“致命错误:所有 goroutine 都处于睡眠状态 - 死锁!”。

我使用两个渠道进行协调,一个用于创建工作,第二个用于发送结果。创建作业后,我关闭输入通道。

我的问题是如何关闭输出通道以便程序可以正确退出。 代码是:

package main

import (
    "bufio"
    "flag"
    "fmt"
    "log"
    "math/rand"
    "os"
    "time"
)

type Work struct {
    id int
    ts time.Duration
}

const (
    NumWorkers = 5000
    NumJobs    = 100000
)

func worker(in <-chan *Work, out chan<- *Work) {
    for w := range in {
        st := time.Now()
        time.Sleep(time.Duration(rand.Int63n(int64(200 * time.Millisecond))))
        w.ts = time.Since(st)
        out <- w
    }
}

func main() {
    wait := flag.Bool("w", false, "wait for <enter> before starting")
    flag.Parse()

    if *wait {
        fmt.Printf("I'm <%d>, press <enter> to continue", os.Getpid())
        reader := bufio.NewReader(os.Stdin)
        reader.ReadString('\n')
    }

    Run()
}

func Run() {
    in, out := make(chan *Work, 100), make(chan *Work, 100)
    for i := 0; i < NumWorkers; i++ {
        go worker(in, out)
    }
    go createJobs(in)
    receiveResults(out)
}

func createJobs(queue chan<- *Work) {
    for i := 0; i < NumJobs; i++ {
        work := &Work{i, 0}
        queue <- work
    }
    close(queue)
}

func receiveResults(completed <-chan *Work) {
    for w := range completed {
        log.Printf("job %d completed in %s", w.id, w.ts)
    }
}

任何帮助表示赞赏:)

【问题讨论】:

  • 打印了很多信息(gorutines 状态)来帮助诊断此类问题

标签: concurrency go


【解决方案1】:

我错过了关于您知道原始答案中死锁原因的部分。

  • 您提到了 WaitGroup,它基本上只是一个信号量
  • 您可以使用另一个“控制”通道,工作人员在完成工作后会使用该通道

-

func worker(ctrl chan<- bool, in <-chan *Work, out chan<- *Work) {
    for w := range in {
        st := time.Now()
        time.Sleep(time.Duration(rand.Int63n(int64(200 * time.Millisecond))))
        w.ts = time.Since(st)
        out <- w
    }
    ctrl <- true
}

func control(ctrl <-chan bool, numWorkers int, out chan<- *Work) {
    for i=0; i<numWorkers; i++ {
        <-ctrl
    }
    close(out)
}

原答案:

您在完成时执行range

for w := range completed {
    log.Printf("job %d completed in %s", w.id, w.ts)
}

但该频道从未关闭

【讨论】:

  • 正确。我正在考虑使用 WaitGroup 来关闭已完成的频道,因为我没有其他方法。
  • 我错过了您描述中的最后一句话。嗯,这是一种可能的解决方案。
  • Arjan,我正在使用你建议的控制通道,非常感谢:)
  • 我用最终代码创建了一个要点:gist.github.com/marcosvm/108b9b65905d82ed2f34
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-06-30
  • 1970-01-01
  • 1970-01-01
  • 2013-11-22
  • 2018-05-28
  • 1970-01-01
相关资源
最近更新 更多