如何在 defer 块中安全地关闭一个通道?

huangapple go评论103阅读模式
英文:

How can one close a channel in a defer block safely?

问题

考虑以下示例:

package main

import (
	"fmt"
	"time"
)

func main() {
	ticker := time.NewTicker(2 * time.Second)
	done := make(chan bool)
	defer func() {
		fmt.Println("exiting..")
		done <- true
		close(done)
	}()
	go func(ticker *time.Ticker, done chan bool) {
		for {
			select {
			case <-done:
				fmt.Println("DONE!")
				break
			case <-ticker.C:
				fmt.Println("TICK!...")
			}
		}
	}(ticker, done)
	time.Sleep(7 * time.Second)
}

等待从done接收的goroutine永远不会接收到消息,我猜测是因为主goroutine在之前就已经结束了。然而,如果我将主goroutine的睡眠时间更改为8秒钟,它会接收到一条消息。为什么睡眠时间会对此有影响?

这是因为有一个一秒钟的差异,它使得goroutine保持活动状态,而没有足够的时间来终止它。

要如何优雅地终止goroutine呢?

英文:

Consider the following example:

package main

import (
	&quot;fmt&quot;
	&quot;time&quot;
)

func main() {
	ticker := time.NewTicker(2 * time.Second)
	done := make(chan bool)
	defer func() {
		fmt.Println(&quot;exiting..&quot;)
		done &lt;- true
		close(done)
	}()
	go func(ticker *time.Ticker, done chan bool) {
		for {
			select {
			case &lt;-done:
				fmt.Println(&quot;DONE!&quot;)
				break
			case &lt;-ticker.C:
				fmt.Println(&quot;TICK!...&quot;)
			}
		}
	}(ticker, done)
	time.Sleep(7 * time.Second)
}

The goroutine waiting to receive from done never receives as (I am guessing) the main goroutine finished beforehand. However if I change the sleep time of the main goroutine to 8 seconds it receives a message; Why is there this dependency on the sleep time?

Is it because there is that second difference that keeps the goroutine alive and the there isn't enough time to kill it?

How would I than kill the goroutine gracefully?

答案1

得分: 0

你需要确保在goroutine完成之前,main函数不会返回。最简单的方法是使用WaitGroup:

var wg sync.WaitGroup
defer wg.Wait()
wg.Add(1)
go func() {
    defer wg.Done()
    // ...

请注意,defer语句按照相反的顺序执行,所以你必须将defer wg.Wait()放在defer close(done)之前,否则会发生死锁。

英文:

You need to ensure that main does not return before the goroutine finishes.
The simplest way to do this is using a WaitGroup:

var wg sync.WaitGroup
defer wg.Wait()
wg.Add(1)
go func() {
    defer wg.Done()
    // …

Note that defers run in reverse order, so you must put defer wg.Wait() before defer close(done), otherwise it will deadlock.

huangapple
  • 本文由 发表于 2022年11月18日 04:21:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/74481636.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定