英文:
all go routines are asleep deadlock
问题
我正在学习并发编程,并且我已经将它作为一个独立的应用程序编写,以便一旦它正常工作,我就可以将其移植到另一个项目中。
我要将它添加到的项目基本上会将一个RowInfo发送到全局的QueueChannel中,然后我的工作线程应该接收这个任务并处理它。如果我将具有相同ID的两个行添加到队列中,并且其中一个正在被工作线程处理,我将从队列中删除重复的行(你可以在调度器中看到我是如何使用"continue"的)。
这个队列/工作线程的代码将在一个阻塞在ListenAndServe上的Web服务器上运行,所以我希望它始终保持运行状态,并且工作线程始终主动寻找任务。我不想关闭通道(除非可能是通过ctrl+C关闭应用程序或其他方式)。我怀疑我得到的错误与不关闭通道有关,因为其他线程提到这个错误的帖子似乎都指示了这一点,但我不确定它与我现有的代码有什么关系。
终端错误输出:
[~/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.dispatcher(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)
// 启动WorkerCount个工作线程
workerCount := 4
for i := 0; i < workerCount; i++ {
go worker(workerChan, exitChan)
}
// 发送测试数据
for i := 0; i < 12; i++ {
QueueChan <- RowInfo{id: int64(i)}
}
// 防止应用程序关闭
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("正在处理行信息 ID:%d 的工作", job.id)
// 完成工作
exitChan <- job.id
}
}
谢谢。
英文:
I'm in the process of learning how to do concurrency, and I've written this as its own app so that I can port it into a different project once it's working.
The project I'm adding it to will basically send in a RowInfo to a global QueueChannel, and then my workers should pick up this work and process it. If I queue two rows with the same ID, and one of them is currently processing by a worker, I'll remove the duplicate row from the queue (as you can see where I do my "continue" in the dispatcher).
This queueing/worker code will be running on a web server blocking on ListenAndServe, so I want it to always remain running and the workers to always remain actively looking for jobs. I don't want to have to close the channels (unless perhaps I ctrl+C'd the app or something). I suspect the error I'm getting has something to do with not closing channels because that's what a lot of the other threads mentioning this error seem to indicate, but I'm not sure how it relates to the code I have exactly.
Terminal error output:
[~/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
Code:
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
}
}
Thank you.
答案1
得分: 5
错误告诉你:所有的goroutine都处于休眠状态,程序已经发生了死锁。
现在为什么所有的goroutine都处于休眠状态呢?让我们逐个检查它们:
worker
goroutine:无限期地等待workerChan
上的新工作,只有在workerChan
关闭时才会退出,每当它等待新工作时都会处于休眠状态。dispatcher
goroutine:无限循环,在两个通道上进行选择。永远不会退出,在select
中等待时处于休眠状态。main
goroutine:在time.Sleep
上无限循环,永远不会退出,并且大部分时间都处于休眠状态。
通常,在这种情况下,你会引入一个chan struct{}
(称为closing
或类似的名称),并将其包含在你的select
语句中。如果你想关闭程序,只需close(closing)
。select
将选择<-closing
选项,然后你返回goroutines。
你还应该添加一个sync.WaitGroup
,以便在所有的goroutine退出时得到通知。
英文:
The error tells you: all goroutines are asleep, the program has deadlocked.
Now why are all your goroutines asleep? Let's check them one by one:
- the
worker
goroutines: Wait indefinitely for new work onworkerChan
, will not exit untilworkerChan
is closed, is asleep whenever it waits for new work - the
dispatcher
goroutine: Loops forever, selecting over two channels. Will never exit, is asleep while waiting in theselect
- the
main
goroutine: Loops forever on atime.Sleep
, will never exit and be asleep most of the time
Typically, in a situation like this, you'd introduce a chan struct{}
(call it closing
or something like that) and inclue it in your select
s. If you want to close the program, just close(closing)
. The select
will choose the <-closing
option, you return the goroutines.
You should also add a sync.WaitGroup
to be notified when all your goroutines have exited.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论