登录
首页 >  Golang >  Go问答

未将填充通道的函数调用嵌入 Goroutine 会导致死锁的原因是什么?

来源:stackoverflow

时间:2024-03-05 12:18:26 444浏览 收藏

目前golang学习网上已经有很多关于Golang的文章了,自己在初次阅读这些文章中,也见识到了很多学习思路;那么本文《未将填充通道的函数调用嵌入 Goroutine 会导致死锁的原因是什么?》,也希望能帮助到大家,如果阅读完后真的对你学习Golang有帮助,欢迎动动手指,评论留言并分享~

问题内容

我知道 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 workersync 来完成它们的工作。在 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 会导致死锁的原因是什么?》的详细内容,更多关于的资料请关注golang学习网公众号!

声明:本文转载于:stackoverflow 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>