猿问

关闭缓冲通道时是否应该将其排空

给定 Go 中(部分)填充的缓冲通道


ch := make(chan *MassiveStruct, n)

for i := 0; i < n; i++ {

    ch <- NewMassiveStruct()

}

是否建议在关闭通道时(由作者)也排空通道,以防读者何时从中读取(例如,数量有限并且他们目前正忙)?那是


close(ch)

for range ch {}

如果通道上还有其他并发阅读器,这样的循环是否保证结束?


上下文:具有固定数量工作器的队列服务,当服务关闭时,它应该停止处理任何排队的东西(但不一定在之后被 GC 处理)。所以我要向工作人员表明服务正在终止。我可以立即排空剩余的“队列”,让 GC 释放分配的资源,我可以读取和忽略工作程序中的值,我可以离开通道,因为读取器正在运行,并将通道设置为 nil 在写入器中GC 清理一切。我不确定哪种方式最干净。


狐的传说
浏览 143回答 3
3回答

FFIVE

这取决于您的程序,但一般来说我倾向于说不(您不需要在关闭频道之前清除频道):如果您关闭频道时频道中有项目,任何仍在从频道阅读的读者都会接收项目,直到通道为空。下面是一个例子:package mainimport (&nbsp; &nbsp; "sync"&nbsp; &nbsp; "time")func main() {&nbsp; &nbsp; var ch = make(chan int, 5)&nbsp; &nbsp; var wg sync.WaitGroup&nbsp; &nbsp; wg.Add(1)&nbsp; &nbsp; for range make([]struct{}, 2) {&nbsp; &nbsp; &nbsp; &nbsp; go func() {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for i := range ch {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; wg.Wait()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; println(i)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; }()&nbsp; &nbsp; }&nbsp; &nbsp; for i := 0; i < 5; i++ {&nbsp; &nbsp; &nbsp; &nbsp; ch <- i&nbsp; &nbsp; }&nbsp; &nbsp; close(ch)&nbsp; &nbsp; wg.Done()&nbsp; &nbsp; time.Sleep(1 * time.Second)}在这里,程序将输出所有项目,尽管通道在任何读者甚至可以从通道读取之前严格关闭。

噜噜哒

有更好的方法来实现您想要实现的目标。您当前的方法只会导致丢弃一些记录,并随机处理其他记录(因为排水循环正在与所有消费者竞争)。这并没有真正解决目标。你想要的是取消。这是Go 并发模式中的一个示例:管道和取消func sq(done <-chan struct{}, in <-chan int) <-chan int {&nbsp; &nbsp; out := make(chan int)&nbsp; &nbsp; go func() {&nbsp; &nbsp; &nbsp; &nbsp; defer close(out)&nbsp; &nbsp; &nbsp; &nbsp; for n := range in {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; select {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case out <- n * n:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case <-done:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }()&nbsp; &nbsp; return out}您将一个done通道传递给所有 goroutine,并在您希望它们都停止处理时关闭它。如果你经常这样做,你可能会发现这个golang.org/x/net/context包很有用,它正式化了这个模式,并添加了一些额外的功能(比如超时)。

动漫人物

我觉得所提供的答案实际上并没有澄清,除了不需要排水也不需要关闭的提示。因此,针对所描述的上下文的以下解决方案对我来说看起来很干净,它终止了工作人员并删除了对他们或相关频道的所有引用,因此,让 GC 清理频道及其内容:type worker struct {&nbsp; &nbsp; submitted chan Task&nbsp; &nbsp; stop&nbsp; &nbsp; &nbsp; chan bool&nbsp; &nbsp; p&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*Processor}// executed in a goroutinefunc (w *worker) run() {&nbsp; &nbsp; for {&nbsp; &nbsp; &nbsp; &nbsp; select {&nbsp; &nbsp; &nbsp; &nbsp; case task := <-w.submitted:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if err := task.Execute(w.p); err != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; logger.Error(err.Error())&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; case <-w.stop:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; logger.Warn("Worker stopped")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}func (p *Processor) Stop() {&nbsp; &nbsp; if atomic.CompareAndSwapInt32(&p.status, running, stopped) {&nbsp; &nbsp; &nbsp; &nbsp; for _, w := range p.workers {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; w.stop <- true&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; // GC all workers as soon as goroutines stop&nbsp; &nbsp; &nbsp; &nbsp; p.workers = nil&nbsp; &nbsp; &nbsp; &nbsp; // GC all published data when workers terminate&nbsp; &nbsp; &nbsp; &nbsp; p.submitted = nil&nbsp; &nbsp; &nbsp; &nbsp; // no need to do the following above:&nbsp; &nbsp; &nbsp; &nbsp; // close(p.submitted)&nbsp; &nbsp; &nbsp; &nbsp; // for range p.submitted {}&nbsp; &nbsp; }}
随时随地看视频慕课网APP

相关分类

Go
我要回答