如何优雅地关闭 Goroutine?

在 Golang 中,可以使用 context.Context 来控制 Goroutine 的生命周期和取消。具体做法是,在启动 Goroutine 时,将一个 context.Context 对象传递给 Goroutine,在 Goroutine 内部使用 select 语句监听 context.Context.Done() 通道,如果该通道被关闭,则表示需要关闭 Goroutine。示例如下:

func worker(ctx context.Context) {
    for {
        select {
        case <-ctx.Done():
            // 收到关闭信号,退出循环
            return
        default:
            // 执行任务
        }
    }
}

func main() {
    ctx, cancel := context.WithCancel(context.Background())

    // 启动 Goroutine
    go worker(ctx)

    // 等待一定时间后关闭 Goroutine
    time.AfterFunc(time.Second * 5, func() {
        cancel()
    })

    // 等待 Goroutine 退出
    time.Sleep(time.Second * 10)
}

下面再来看一个使用 Context 来优雅地关闭 Goroutine 的示例代码:

golang www.itzhimei.com代码
package main

import (
    "context"
    "fmt"
    "time"
)

func worker(ctx context.Context) {
    for {
        select {
        case <-ctx.Done():
            fmt.Println("worker: context cancelled, exiting...")
            return
        default:
            fmt.Println("worker: doing work...")
            time.Sleep(1 * time.Second)
        }
    }
}

func main() {
    ctx, cancel := context.WithCancel(context.Background())
    go worker(ctx)

    // 等待 5 秒后取消 Context
    time.Sleep(5 * time.Second)
    cancel()

    // 等待 worker  goroutine 退出
    <-ctx.Done()
    fmt.Println("main: worker exited.")
}

在上面的代码中,我们首先使用 context.WithCancel 函数创建了一个携带取消信号的 Context,并将其传递给 worker 函数。在 worker 函数中,我们使用 select 语句来检查 Context 是否被取消,如果被取消,则立即退出。

在 main 函数中,我们等待 5 秒后调用 cancel 函数来取消 Context,此时 worker 函数中的 Context.Done() 通道会被关闭,worker 函数会收到取消信号并退出。

最后,我们使用 <-ctx.Done() 来等待 worker 函数退出,确保所有的 Goroutine 都已经结束。

这样,我们就可以优雅地关闭 Goroutine,避免资源泄露和其他潜在的问题。