【问题标题】:Should idle threads be left around in long running process?是否应该在长时间运行的过程中留下空闲线程?
【发布时间】:2016-04-09 13:01:10
【问题描述】:

我正在创建一个旨在长期运行并监听工作的围棋程序。当它收到请求时,它会在进程队列上运行工作。

我是 golang 和系统编程的新手,所以我的问题是:我应该在程序启动时启动进程队列(带有多个空闲的工作线程)(它们只会坐在那里直到工作开始)还是应该我在工作到达时启动它们并在完成时将它们关闭?

我不清楚多个空闲线程会产生的整体系统影响,但我假设由于它们处于空闲状态,因此在工作到达之前不会产生任何影响。话虽如此,我想确保我的程序是“好邻居”并且尽可能高效。

--编辑--

为了澄清,“进程池”是一组等待通道上工作的工作程序。它们应该在工作到达时启动/停止,还是在程序启动时启动并等待工作进入?

【问题讨论】:

  • golang中没有“创建线程”的概念。如果你在谈论 goroutines;它们非常便宜。根据需要启动它们。
  • goroutines 在其函数返回时结束。你通常不会有“空闲”的 goroutine,它们要么正在做某事,要么已经返回。
  • 是的,我为不正确的命名道歉,goroutine 就是我所说的。那么,在通道上等待消息的长时间运行的“空闲” gorutine 池没有问题吗?
  • @SjB 你如何获得一个空闲的 goroutine 池。这听起来不对。
  • Goroutines 不需要操作系统线程,并且堆栈非常小。有一个池等待消息没有问题,但是因为它们生成起来非常便宜,所以“工作池”通常不是构建程序的正确方法。

标签: multithreading go daemon


【解决方案1】:

无论哪种方式,成本都非常低。 goroutines 不需要单独的 OS 线程,并且在阻塞通道接收时几乎不消耗任何资源,而且启动起来的成本非常很少,因此也没有充分的理由让它们保持打开状态。

我的代码很少使用工作池。通常,我的生产者会为它产生的每个工作单元生成一个 goroutine,并将其与响应通道一起直接传递,然后生成一个“侦听器”,它对工作输出进行一些格式化并将所有响应传递回主线程。对我来说常见的模式如下:

func Foo(input []interface{}) resp chan interface{} {
    var wg sync.WaitGroup
    resp := make(chan interface{})
    listen := make(chan interface{})
    theWork := makeWork(input)
    // do work
    for _, unitOfWork := range theWork {
        wg.Add(1)
        go func() {
            // doWork has signature:
            //   func doWork(w interface{}, ch chan interface{})
            doWork(unitOfWork, listen)
            wg.Done()
        }()
    }
    // format the output of listen chan and send to resp chan
    // then close resp chan so main can continue
    go func() {
        for r := range listen {
            resp <- doFormatting(r)
        }
        close(resp)
    }()
    // close listen chan after work is done
    go func() {
        wg.Wait()
        close(listen)
    }()
    return resp
}

然后我的主函数将一些输入传递给它并在响应通道上监听

func main() {
    loremipsum := []string{"foo", "bar", "spam", "eggs"}
    response := Foo(loremipsum)
    for output := range response {
        fmt.Println(output)
    }
}

【讨论】:

    【解决方案2】:

    任务队列和等待工作人员的模式在 Go 中很常见。 Goroutines 很便宜,但执行顺序是不确定的。因此,如果您希望您的系统行为是可预测的,您最好通过循环或其他方式请求的无缓冲通道控制工作人员与主例程的会合。否则它们中的一些可以生成但保持空闲这是合法的。

    【讨论】:

    • 谢谢,这是我想要的模式:工作完全相同,不需要按特定顺序执行
    【解决方案3】:

    首先,您不能使用标准 Go 库创建线程。在 Go 世界中,您应该使用称为 green threads 的 goroutine。

    通常你不应该生成“可重用”的 goroutine。它们的创建成本很低,因此在工作任务到达时按需创建它们,并在工作完成后立即完成(从 goroutine 返回)。

    还可以毫不犹豫地创建嵌套的 goroutine。一般来说,如果你觉得你应该以并发的方式做某事并且不要试图重用它们,因为这没有任何意义。

    【讨论】:

    • 谢谢,但我正在寻找一个任务队列模式,其中有可重用的工作 goroutine 等待传入的工作。关于绿色线程的链接非常有帮助!
    • 是的,但你也说过“等待通道上的工作”,所以我的建议是编写一个代码来消耗来自通道的工作任务(这是同步的)并在工作到达时生成一个 goroutine不必费心重复使用旧的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-26
    • 2011-11-27
    • 2022-08-20
    • 2023-03-04
    相关资源
    最近更新 更多