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

c86crjj0  于 2023-04-03  发布在  Go
关注(0)|答案(2)|浏览(95)

我知道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元素的处理应该停止,这就是为什么我有两个通道,一个指示进程开始,另一个指示进程结束。
当运行我的代码时,我得到以下错误:
致命错误:所有的goroutine都处于休眠状态-死锁!
如果我修改createJobs的调用方式,将其 Package 在一个goroutine中,如下所示:

go func() {
        createJobs(jobs)
    }()

那么我的代码就能正常运行。
我只想知道为什么会这样。我是说:main例程正在执行,然后它会调用createJobs(无回绕),所以main例程应该被阻塞,直到这个调用结束。一旦createJobs结束,这意味着通道中有元素。main继续执行,并启动其他goroutine workersync来完成它们的工作。在main结束之前,我只是简单地添加了一个sleeper,以便为之前创建的goroutine给予时间来完成。
我不是在问这个问题的其他解决方案,我只是想知道当createJobs发生在goroutine之外时会发生什么。

xfb7svmp

xfb7svmp1#

你将jobs声明为一个无缓冲的通道,然后试图同步地将20个值推入其中。这将在你调用createJobs(jobs)时阻塞你的main函数。
将第13行改为:

jobs := make(chan int, 20)

……将打破僵局。

rfbsl7qr

rfbsl7qr2#

以前的解决方案完全有效。但您也可以使用

go func() {
        createJobs(jobs)
    }()

如果您仍然想保持默认通道大小。

相关问题