当前位置: 首页 > 知识库问答 >
问题:

如何在处理结果时正确关闭Goroutines中的共享通道

叶鸿煊
2023-03-14

我试图找到正确的方法来使用工人围棋例程产生的结果,同时在所有工作完成后优雅地退出结果循环。为了说明,我做了以下示例。我的真实世界案例与这个例子略有不同,因为我不知道每个工人围棋例程将返回多少“工作”,显然这些for循环执行固定数量的结果(5)。

我是goroutines和频道的新手,但以下是我所理解的基本租户;

  • 只有发送者才能关闭频道
  • 在通道上执行范围将继续,直到通道关闭
package main

import (
    "fmt"
    "sync"
)

func worker1(r chan string, wg *sync.WaitGroup) {
    for i := 0; i < 5; i++ {
        r <- fmt.Sprintf("1.%d", i)
    }

    wg.Done()
}

func worker2(r chan string, wg *sync.WaitGroup) {
    for i := 0; i < 5; i++ {
        r <- fmt.Sprintf("2.%d", i)
    }

    wg.Done()
}

func main() {
    var wg sync.WaitGroup

    r := make(chan string)
    wg.Add(2)
    go worker1(r, &wg)
    go worker2(r, &wg)

    for i := range r {
        fmt.Printf("Got job result: %s\n", i)
    }

    wg.Wait()
}

这个例子是死锁,因为范围循环永远不会退出,因为通道永远不会关闭。工作完成后,我可以在通道上执行关闭操作(即用close(r)替换wg.done()),但当另一个worker goroutine试图向已经关闭的通道发送进一步结果时,我会感到恐慌。

最后我想我可以移动wg了。Wait()在结果循环上方,完成后关闭通道,然后开始打印结果,但这意味着在所有线程上的所有工作完成之前,我无法打印任何结果。

在所有工作线程完成后,如何正确地优雅地退出结果循环,同时也不要等到所有工作完成后才开始打印结果?

共有1个答案

吕俊哲
2023-03-14

我已经编辑了你的代码,可以在没有死锁的情况下工作。问题是,通道上的接收阻塞了主线程,而您的两个goroutine都没有发送更多数据。

解决方案运行一个新的goroutine,一旦WaitGroup完成,它就会关闭结果通道。

package main

import (
    "fmt"
    "sync"
)

func worker1(r chan string, wg *sync.WaitGroup) {
    for i := 0; i < 5; i++ {
        r <- fmt.Sprintf("1.%d", i)
    }

    wg.Done()
}

func worker2(r chan string, wg *sync.WaitGroup) {
    for i := 0; i < 5; i++ {
        r <- fmt.Sprintf("2.%d", i)
    }

    wg.Done()
}

func main() {
    var wg sync.WaitGroup

    r := make(chan string)
    wg.Add(2)
    go worker1(r, &wg)
    go worker2(r, &wg)

    go func() {
        defer close(r)
        wg.Wait()
    }()

    for i := range r {
        fmt.Printf("Got job result: %s\n", i)
    }
}

(去游乐场)

 类似资料:
  • 问题内容: 我正在Python中进行多处理实验,并试图在两个进程之间共享一个字符串数组。这是我的python代码: 运行脚本时,我看到该脚本已正确填充,并且可在中找到,但不能在中找到。结果如下: 我忽略了什么吗? 预先感谢您的反馈。:) 问题答案: 我的猜测是: 存储3个指针。将它们分配给当前进程之外没有意义的内存地址。尝试访问此时包含垃圾邮件的无意义地址。 分配对所有过程都有意义的值似乎有帮助:

  • 有人能帮我吗?

  • 我正在学习Spring Core认证,在提供的学习材料中,我对这个问题有一些疑问: 关闭应用程序上下文的首选方法是什么? 我知道如果我有这样的东西: 通过在上下文对象上使用close()方法,ApplicationContext被关闭,应用程序被销毁。 但我认为这不是我必须做的最好的方式。 阅读官方留档,我发现我也可以这样做: 在JVM中注册一个关闭挂钩,因此JVM将在JVM退出之前触发Sprin

  • 问题内容: 我希望我的计时器一次执行一次actionPerformed方法(5秒一次),但是它多次在控制台“ Hello”中写入: 如何产生想要的效果?谢谢 问题答案: 如前所述,最好使用,但也可以在开始之前使用setRepeats():

  • 问题内容: 我是Go菜鸟,无法找到在Go中打开mysql连接然后在http处理程序之间共享它的完整示例。到目前为止,这是我的代码,如何使用在HomeHandler中的main()中打开的db连接? 问题答案: 数据库/ sql程序包会自动为您管理连接池。 返回 代表连接池 而不是单个连接的句柄。如果池中的所有连接都忙,则数据库/ sql软件包会自动打开一个新连接。 将其应用于代码意味着,您只需要共