当填充通道的函数调用未嵌入Goroutine中时,会出现死锁的原因是因为通道的发送和接收操作是阻塞的。如果在主Goroutine中调用填充通道的函数,并且该函数内部没有将填充操作放入新的Goroutine中运行,那么主Goroutine会一直等待通道有足够的空间来接收数据,而填充操作又无法进行,从而导致死锁的产生。因此,为了避免死锁,我们需要在填充通道的操作中使用Goroutine来进行并发执行,以确保填充操作和接收操作可以同时进行。
问题内容
我知道 sync
包及其 waitgroup
选项,我不想将其用于此测试。我正在测试一种信号量。
所以我有:
package main
import (
"fmt"
"os"
"time"
)
func main() {
fmt.print("wassap")
jobs := make(chan int)
processstarted := make(chan struct{}, 1)
processcompleted := make(chan struct{}, 1)
createjobs(jobs)
go func() {
worker(jobs, processstarted, processcompleted)
}()
go func() {
sync(processstarted, processcompleted)
}()
time.sleep(3600 * time.second)
fmt.print("\nend of main...")
interrupt := make(chan os.signal)
<-interrupt
}
func createjobs(jobs chan<- int) {
defer close(jobs)
for i := 1; i < 20; i++ {
jobs <- i
}
}
func worker(jobs <-chan int, processstarted <-chan struct{}, processcompleted <-chan struct{}) {
for {
select {
case i := <-jobs:
fmt.printf("\nfetching job #%d from channel", i)
time.sleep(2 * time.second)
case <-processstarted:
fmt.print("\nprocess started. waiting for it to be completed")
<-processcompleted
fmt.print("\nprocess completed")
}
}
}
func sync(processstarted chan<- struct{}, processcompleted chan<- struct{}) {
// acquire semaphore. send signal to channel to indicate that it is busy
processstarted <- struct{}{}
for i := 1; i < 5; i++ {
fmt.printf("\nprocessing %d", i)
time.sleep(5 * time.second)
}
// release semaphore
processcompleted <- struct{}{}
}
我想要测试的非常简单:我有一个 createjobs
函数,其唯一目的是将元素添加到通道,在本例中是一个 int 通道。然后我有一个 worker
将从该通道中提取对象并在提取下一个元素之前休眠 2 秒。
现在,还有同步功能。该函数的唯一目的是模拟 worker
运行时启动的进程。如果此进程处于活动状态,则在 sync
结束时应停止处理 jobs
元素,这就是为什么我有两个通道,一个表示进程已启动,另一个表示进程结束。
运行我的代码时出现以下错误:
fatal error: all goroutines are asleep - deadlock!
如果我修改 createjobs
的调用方式,将其包装在如下所示的 goroutine 中:
go func() {
createJobs(jobs)
}()
然后我的代码运行正确。
我只是想了解为什么会发生这种情况。我的意思是: main
例程正在执行,然后它调用 createjobs
(无换行),因此 main
例程应该被阻止,直到此调用结束。一旦 createjobs
结束,就说明通道中有元素了。 main
继续执行并启动其他 goroutine worker
和 sync
来完成它们的工作。在 main
结束之前,我只是添加一个睡眠程序,以便为之前创建的 goroutine 提供完成时间。
我不是在询问这个问题的其他解决方案,我只是想知道当 createjobs
发生在 goroutine 之外时会发生什么。
解决方法
您将 jobs
声明为无缓冲通道,然后尝试将 20 个值同步推入其中。当您调用 createjobs(jobs)
时,这将阻止您的主函数。
将第 13 行更改为:
jobs := make(chan int, 20)
...将解决僵局。
编辑 - 评论中要求的澄清:
无缓冲通道没有容量,并且会阻止生产者的执行,直到消费者收到消息为止。
无缓冲通道的一个很好的类比是管道,在本例中,过程如下所示:
+------------------+ +------------+ +-------------+
| PRODUCER | | PIPE | | CONSUMER |
| +---->| +----->| |
| createJobs(jobs) | | unbuffered | | worker(...) |
| | | channel | | |
+------------------+ +------------+ +-------------+
发生死锁是因为 createjobs(jobs)
被同步调用,并且还没有消费者在运行。
当在 goroutine 中调用函数(producer)时它可以工作,因为基本上插入通道和读取通道是并行发生的?
是的。如果生产者被异步调用,它不会阻塞 main()
函数,因此消费者也将有机会被调用。在这种情况下,生产者将一一推送其所有任务,就像工作人员一一消费它们一样。
以上就是当填充通道的函数调用未嵌入 Goroutine 中时,为什么会出现死锁?的详细内容,更多请关注编程网其它相关文章!