Go

时间:2016-11-03 15:13:11

标签: multithreading go concurrency channel goroutine

我正在尝试在go中实现一个简单的工作池并继续遇到问题。我想做的就是有一定数量的工人在做更多的工作之前做了一定数量的工作。我使用的代码类似于:

    jobs := make(chan imageMessage, 1)
    results := make(chan imageMessage, 1)

    for w := 0; w < 2; w++ {
        go worker(jobs, results)
    }

    for j := 0; j < len(images); j++ {
        jobs <- imageMessage{path: paths[j], img: images[j]}
    }
    close(jobs)

    for r := 0; r < len(images); r++ {
        <-results
    }
}

func worker(jobs <-chan imageMessage, results chan<- imageMessage) {
    for j := range jobs {
        processImage(j.path, j.img)
        results <- j
    }
}

我的理解是,这应该创造2个工人,可以一次完成1个“事物”,并且在完成这一件事之前将继续获得更多工作,直到没有别的事可做。但是,我得到fatal error: all goroutines are asleep - deadlock!

如果我将缓冲区设置为像100这样大的东西,这可行,但我希望能够限制一次完成的工作。

我觉得我很亲密,但显然错过了一些东西。

1 个答案:

答案 0 :(得分:3)

问题是你只能开始排水&#34;成功发送results频道上的所有作业后,jobs频道。但是,为了能够发送所有作业,jobs通道必须具有足够大的缓冲区,或者工作者goroutine必须能够从中消耗作业。

但是,一名工作人员在完成工作之前会先尝试下一个工作,然后在results频道上发送结果。如果results频道的缓冲区已满,则发送结果将被阻止。

但最后一部分 - 工作人员goroutine在发送结果时被阻止 - 只能是&#34; unblocked&#34;通过results渠道接收 - 您不会发送所有工作。如果jobs频道和results频道的缓冲区无法保留所有作业,则会出现死锁。这也解释了为什么它可以将缓冲区大小增加到一个大值:如果作业可以适应缓冲区,则不会发生死锁,并且在所有作业成功发送后,最终循环将消耗results信道。

解决方案?在自己的goroutine中运行生成和发送作业,这样您就可以立即开始从results频道&#34;&#34;无需等待发送所有作业,这意味着工作人员goroutines不会永远被阻止尝试发送结果:

go func() {
    for j := 0; j < len(images); j++ {
        jobs <- imageMessage{path: paths[j], img: images[j]}
    }
    close(jobs)
}()

Go Playground上尝试。

另请查看Is this an idiomatic worker thread pool in Go?

中的类似实现