3

我正在构建一个使用 goroutines 的“工作池”的 Go 应用程序,最初我启动池创建了一些工作程序。我想知道多核处理器中的最佳工作人员数量是多少,例如在具有 4 核的 CPU 中?我目前正在使用以下方法:

    // init pool
    numCPUs := runtime.NumCPU()

    runtime.GOMAXPROCS(numCPUs + 1) // numCPUs hot threads + one for async tasks.
    maxWorkers := numCPUs * 4

    jobQueue := make(chan job.Job)

    module := Module{
        Dispatcher: job.NewWorkerPool(maxWorkers),
        JobQueue:   jobQueue,
        Router:     router,
    }

    // A buffered channel that we can send work requests on.
    module.Dispatcher.Run(jobQueue)

完整的实现如下

job.NewWorkerPool(maxWorkers) 和 module.Dispatcher.Run(jobQueue)

我使用工作池的用例:我有一个服务,它接受请求并调用多个外部 API 并将它们的结果聚合到一个响应中。每个调用都可以独立于其他调用,因为结果的顺序无关紧要。我将调用分派到工作池,每个调用都在一个可用的 goroutine 中以异步方式完成。一旦工作线程完成,我的“请求”线程会在获取和聚合结果的同时继续监听返回通道。完成所有操作后,最终聚合结果将作为响应返回。由于每个外部 API 调用可能会呈现可变响应时间,因此某些调用可以比其他调用更早完成。

4

1 回答 1

10

您的示例代码中的注释表明您可能将GOMAXPROCS和 工作池的两个概念混为一谈。这两个概念在 Go 中是完全不同的。

  1. GOMAXPROCS设置 Go 运行时将使用的最大 CPU 线程数。这默认为系统上发现的 CPU 内核数,并且几乎不应该更改。我能想到的唯一一次更改是,如果您出于某种原因想明确限制 Go 程序使用的 CPU 数量少于可用的 CPU,那么您可以将其设置为 1,例如,即使在 4-核心CPU。这应该只在极少数情况下才重要。

    TL;博士; 切勿runtime.GOMAXPROCS手动设置。

  2. Go 中的工作池是一组 goroutine,它们在作业到达时处理它们。在 Go 中有不同的方法来处理工作池。

    你应该使用多少工人?没有客观的答案。可能唯一知道的方法是对各种配置进行基准测试,直到找到满足您要求的配置。

    作为一个简单的例子,假设您的工作池正在做一些 CPU 密集型的事情。在这种情况下,您可能希望每个 CPU 一个工作人员。

    不过,作为一个更有可能的例子,假设您的工作人员正在做更多 I/O 绑定的事情——例如读取 HTTP 请求或通过 SMTP 发送电子邮件。在这种情况下,您可以合理地处理每个 CPU 数十甚至数千个工作人员。

    还有一个问题是你是否应该使用工作池。Go 中的大多数问题根本不需要工作池。我曾参与过数十个生产 Go 程序,但从未在其中任何一个中使用过工作池。我还编写了很多次一次性使用的 Go 工具,并且可能只使用了一次工作池。

最后,GOMAXPROCS与工作池相关的唯一方式与 goroutine 相关的方式相同GOMAXPROCS。从文档

GOMAXPROCS 变量限制了可以同时执行用户级 Go 代码的操作系统线程的数量。代表 Go 代码在系统调用中可以阻塞的线程数没有限制;这些不计入 GOMAXPROCS 限制。这个包的 GOMAXPROCS 函数查询和更改限制。

从这个简单的描述中,很容易看出可能有更多(可能是数十万......或更多)goroutine,而不是GOMAXPROCS——GOMAXPROCS仅限制有多少“可以同时执行用户级 Go 代码的操作系统线程”——目前不执行用户级 Go 代码的 goroutine 不算在内。并且在 I/O 绑定的 goroutines(例如等待网络响应的那些)中不执行代码。因此,理论上您的 goroutine 最大数量仅受系统可用内存的限制。

于 2017-12-24T11:00:19.943 回答