【问题标题】:Killing a goroutine once theres a new value in a channel一旦通道中有新值,就杀死一个 goroutine
【发布时间】:2021-09-11 16:21:33
【问题描述】:

对于通道中的每个新值,都会产生一个 goroutine。当通道中有新值时,我希望启动一个新的 goroutine,并杀死旧的 goroutine。我怀疑我的代码正在杀死新的 goroutine 并保持第一个 goroutine 存活。我该如何解决这个问题?

func Start() {
    go func() {
        quit := make(chan bool, 1)
        for nbp := range poll() {
            quit <- true
            go create(nbp, quit)
        }
    }()
}

func create(nbp map[string]string, , quit chan bool) {
    for {
        select {
        case <-quit:
            fmt.Println("quiting this goroutine!")
            return
        default:
            for k, v := range nbp {
            ...
            }
            time.Sleep(3 * time.Second)
        }
    }
}

【问题讨论】:

  • 因为你的频道是缓冲的,你可以在没有阻塞的情况下推送第一个项目,因此,当例程在go create(nbp, quit) 产生时,它会进入选择并在退出频道中找到一个值。因此,它退出了......
  • 当我从缓冲区中删除 1 时,什么也没有发生......
  • 是的,它会阻止等待读取它的内容。
  • 介意我问:为什么?通道和例程是卸载一些要同时完成的工作的好工具。如果您等待通道上的新值,终止一个例程并在其位置启动完全相同的例程,为什么不简单地保持第一个例程运行,并在新数据进入时处理它?
  • @testing495 我已经发布了一个包含 3 个选项的答案。最后一个是我将如何去做。因为它使用循环 (for nbp := range ch),所以旧地图无论如何都不会保存在内存中,并且不需要使用 bool 的通道(实际上应该是 struct{},因为它是 0 字节类型) .

标签: go channel goroutine


【解决方案1】:

一个简单的解决方法如下所示

package main

func Start() {
    go func() {
        var i int
        quit := make(chan bool)
        for nbp := range poll() {
            if i > 0 {
                quit <- true
            }
            i++
            go create(nbp, quit)
        }
    }()
}

func create(nbp map[string]string, quit chan bool) {
    for {
        select {
        case <-quit:
            fmt.Println("quiting this goroutine!")
            return
        default:
            for k, v := range nbp {
            //...
            }
            time.Sleep(3 * time.Second)
        }
    }
}

【讨论】:

    【解决方案2】:

    因此,查看您提供的代码,您希望在每次poll 提供新数据时启动一个新例程。这很简单:

    func Start() {
        for nbp := range poll() {
            go create(nbp)
        }
    }
    
    func create(nbp map[string]string) {
        // do stuff here
    }
    

    好的,但是使用这种方法,您可能会同时产生大量例程。您正在使用的quit 频道建议您只想在前一个例程完成后生成一个新例程。同样,这很容易实现:

    func Start() {
        ch := make(chan struct{}) // struct{} is 0 bytes in size as per spec
        defer close(ch) // close channel when done
        for nbp := range poll() {
            go create(nbp, ch)
            ch <- struct{}{} // blocks until the routine has read from the channel
        }
    }
    
    func create(nbp map[string]string, ch <-chan struct{}) {
        for k, v := range nbp {
            // ... do stuff
        }
        time.Sleep(3 * time.Second)
        <-ch // read to unblock Start
    }
    

    太好了,但现在我们只是按顺序做事,并使用一个毫无意义的渠道来这样做......为什么不简单地这样做:

    func Start() {
        ch := make(chan map[string]string) // data in the channel
        defer close(ch) // close channel when done
        go create(ch) // start the routine reading data from the channel
        for nbp := range poll() {
            ch <- nbp // put data on channel, blocks until routine reads from the channel
            time.Sleep(3 * time.Second) // sleep here
        }
    }
    
    func create(nbp <-chan map[string]string) {
        for nbp := range ch { // read from channel
            // and process sequentially
            for k, v := range nbp {
                // ... do stuff
            }
        }
    }
    

    我将 sleep 移到写入通道的循环的原因是,在第一次迭代时,例程将立即从通道中读取(它还没有做任何事情),并取消阻塞 poll() 循环。这将导致快速连续两次调用poll()。将睡眠移出例行程序可确保您在第一次和第二次通话之间至少有 3 秒的时间。之后,行为几乎相同。我说得差不多了,因为您不必“打扰”运行时和调度程序,以便每次都释放与create 例程相关的资源,并在之后立即安排一个新例程。

    【讨论】:

    • 所以我在运行它时遇到的一个问题是 crate 的主体不会在永远循环中运行,因此不会对新配置重复 ...do stuff 功能已阅读。我目前正在尝试查看是否可以在自己中添加永久循环
    • @testing495 好吧,没有看到你在for k, v := range nbp 循环中做了什么,我不知道到底是什么问题。 create 函数确实在我提供的 sn-p 中永远运行(或至少:直到通道关闭)......
    • v 的值获取一个 ETag。这需要每 3 秒完成一次,但是在您的代码中,我相信它只会获取 ETag 一次
    • @testing495 你必须展示你是如何获取它的。我嘲笑了一些我认为类似于您的设置here 的东西,并且所有值都很好地通过了。我必须说:如果poll() 确实像我想的那样返回了一个频道,我可能甚至不会费心将它包装在Start 函数中,而是直接从我的create 例程中调用poll()
    猜你喜欢
    • 1970-01-01
    • 2020-07-15
    • 2014-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-07
    • 2019-04-08
    • 1970-01-01
    相关资源
    最近更新 更多