【问题标题】:all go routines are asleep deadlock所有的 goroutine 都在休眠死锁
【发布时间】:2016-02-11 08:19:09
【问题描述】:

我正在学习如何进行并发,我已经将其编写为自己的应用程序,以便在它工作后可以将其移植到不同的项目中。

我要添加它的项目基本上会将 RowInfo 发送到全局 QueueChannel,然后我的工作人员应该接手这项工作并进行处理。如果我将具有相同 ID 的两行排队,其中一个当前正在由工作人员处理,我将从队列中删除重复的行(如您所见,我在调度程序中执行“继续”操作)。

这个排队/工作代码将在一个阻塞 ListenAndServe 的网络服务器上运行,所以我希望它始终保持运行,并且工作人员始终保持积极寻找工作。我不想关闭频道(除非我 ctrl+C'd 应用程序或其他东西)。我怀疑我遇到的错误与不关闭频道有关,因为很多其他提到此错误的线程似乎都表明了这一点,但我不确定它与我所拥有的代码有何关系。

终端错误输出:

[~/go/src/github.com/zzz/asynch]> go run main.go
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main()
    /home/zzz/go/src/github.com/zzz/asynch/main.go:29 +0x14b

goroutine 5 [select]:
main.diszzzcher(0xc82001a120, 0xc82001a180, 0xc82001a1e0)
    /home/zzz/go/src/github.com/zzz/asynch/main.go:42 +0x21a
created by main.main
    /home/zzz/go/src/github.com/zzz/asynch/main.go:19 +0xb1

goroutine 6 [chan receive]:
main.worker(0xc82001a180, 0xc82001a1e0)
    /home/zzz/go/src/github.com/zzz/asynch/main.go:55 +0x54
created by main.main
    /home/zzz/go/src/github.com/zzz/asynch/main.go:24 +0xf7

goroutine 7 [chan receive]:
main.worker(0xc82001a180, 0xc82001a1e0)
    /home/zzz/go/src/github.com/zzz/asynch/main.go:55 +0x54
created by main.main
    /home/zzz/go/src/github.com/zzz/asynch/main.go:24 +0xf7

goroutine 8 [chan receive]:
main.worker(0xc82001a180, 0xc82001a1e0)
    /home/zzz/go/src/github.com/zzz/asynch/main.go:55 +0x54
created by main.main
    /home/zzz/go/src/github.com/zzz/asynch/main.go:24 +0xf7

goroutine 9 [chan receive]:
main.worker(0xc82001a180, 0xc82001a1e0)
    /home/zzz/go/src/github.com/zzz/asynch/main.go:55 +0x54
created by main.main
    /home/zzz/go/src/github.com/zzz/asynch/main.go:24 +0xf7
exit status 2

代码:

package main

import (
    "log"
    "time"
)

type RowInfo struct {
    id int64
}

var QueueChan chan RowInfo

func main() {
    QueueChan := make(chan RowInfo)
    workerChan := make(chan RowInfo)
    exitChan := make(chan int64)

    go dispatcher(QueueChan, workerChan, exitChan)

    // Start WorkerCount number of workers
    workerCount := 4
    for i := 0; i < workerCount; i++ {
        go worker(workerChan, exitChan)
    }

    // Send test data
    for i := 0; i < 12; i++ {
        QueueChan <- RowInfo{id: int64(i)}
    }

    // Prevent app close
    for {
        time.Sleep(1 * time.Second)
    }
}

func dispatcher(queueChan, workerChan chan RowInfo, exitChan chan int64) {
    state := make(map[int64]bool)

    for {
        select {
        case job := <-QueueChan:
            if state[job.id] == true {
                continue
            }
            workerChan <- job
        case result := <-exitChan:
            state[result] = false
        }
    }
}

func worker(workerChan chan RowInfo, exitChan chan int64) {
    for job := range workerChan {
        log.Printf("Doing work on job rowInfo ID: %d", job.id)

        // Finish job
        exitChan <- job.id
    }
}

谢谢。

【问题讨论】:

    标签: multithreading go concurrency


    【解决方案1】:

    错误告诉你:所有goroutines都处于休眠状态,程序已经死锁。

    现在为什么你所有的 goroutine 都睡着了?让我们一一检查:

    • worker goroutines:无限期地等待workerChan 上的新工作,直到workerChan 关闭才会退出,等待新工作时处于休眠状态
    • dispatcher goroutine:永远循环,选择两个通道。永远不会退出,在select 等待时睡着了
    • main goroutine:在time.Sleep 上永远循环,永远不会退出并且大部分时间都处于休眠状态

    通常,在这种情况下,您会引入chan struct{}(称为closing 或类似名称)并将其包含在您的selects 中。如果你想关闭程序,只需close(closing)select 将选择 &lt;-closing 选项,您返回 goroutines。 您还应该添加一个sync.WaitGroup,以便在您的所有 goroutine 退出时收到通知。

    【讨论】:

    • 谢谢。那么,为什么 Go 会因此对我生气呢?因为我的程序永远不会退出并无限期地休眠? Go 很不高兴,因为我的调度程序函数有一个无限循环,我的 main 有一个无限循环,或两者兼而有之,这是出于某种原因违反规则吗?
    • 好吧,Go 告诉你你的程序死锁了,因为这通常是不受欢迎的行为。可能会发生所有 goroutine 都处于休眠状态而没有死锁的情况(例如在读取网络流时),因此运行时不会抱怨这一点。但是,在您的情况下,您的程序无法继续运行
    • 对。所以我认为当我将它添加到我的其他项目中时它实际上不会成为问题,但现在它是。同时,我会像你建议的那样添加一个关闭频道和一个 WaitGroup,希望这能解决我在这里面临的所有问题。再次感谢:)
    猜你喜欢
    • 1970-01-01
    • 2014-02-22
    • 2021-06-30
    • 1970-01-01
    • 1970-01-01
    • 2013-11-22
    • 2018-05-28
    • 1970-01-01
    相关资源
    最近更新 更多