所有的Go协程都处于休眠状态,发生了死锁。

huangapple go评论70阅读模式
英文:

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]&gt; 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 (
	&quot;log&quot;
	&quot;time&quot;
)

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 &lt; workerCount; i++ {
		go worker(workerChan, exitChan)
	}

	// Send test data
	for i := 0; i &lt; 12; i++ {
		QueueChan &lt;- 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 := &lt;-QueueChan:
			if state[job.id] == true {
				continue
			}
			workerChan &lt;- job
		case result := &lt;-exitChan:
			state[result] = false
		}
	}
}

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

		// Finish job
		exitChan &lt;- 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 on workerChan, will not exit until workerChan 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 the select
  • the main goroutine: Loops forever on a time.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 selects. If you want to close the program, just close(closing). The select will choose the &lt;-closing option, you return the goroutines.
You should also add a sync.WaitGroup to be notified when all your goroutines have exited.

huangapple
  • 本文由 发表于 2016年2月11日 16:19:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/35334035.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定