跳转到主内容
极星编程网:以代码为星,赴技术山海!

当填充通道的函数调用未嵌入 Goroutine 中时,为什么会出现死锁?

当填充通道的函数调用未嵌入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()
函数,因此消费者也将有机会被调用。在这种情况下,生产者将一一推送其所有任务,就像工作人员一一消费它们一样。

相关文章