MST

星途 面试题库

面试题:Go语言Goroutine定时任务的优化实现

假设要在Go语言中使用Goroutine实现一个定时任务,该任务每隔10秒执行一次某个复杂的业务逻辑(例如从数据库读取数据、处理数据并写入文件)。要求考虑资源的合理利用和任务执行的稳定性,避免内存泄漏等问题。请给出代码实现思路以及关键代码片段,并解释如何优雅地停止这个定时任务。
23.5万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

代码实现思路

  1. 使用 time.Ticker 来实现定时触发。time.Ticker 会按照设定的时间间隔向其 C 通道发送时间值。
  2. Goroutine 中执行复杂业务逻辑,每次从 time.Ticker 的通道接收时间值时,执行一次业务逻辑。
  3. 为了优雅地停止定时任务,引入一个 context.Context 来管理 Goroutine 的生命周期。context.Context 可以用于通知 Goroutine 停止执行。

关键代码片段

package main

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

// 模拟复杂业务逻辑
func complexBusinessLogic(ctx context.Context) error {
    // 从数据库读取数据
    // 处理数据
    // 写入文件
    file, err := os.OpenFile("output.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
    if err != nil {
        return err
    }
    defer file.Close()

    _, err = file.WriteString("Data processed at " + time.Now().Format(time.RFC3339) + "\n")
    if err != nil {
        return err
    }
    return nil
}

func main() {
    ctx, cancel := context.WithCancel(context.Background())
    ticker := time.NewTicker(10 * time.Second)
    defer ticker.Stop()

    go func() {
        for {
            select {
            case <-ticker.C:
                err := complexBusinessLogic(ctx)
                if err != nil {
                    fmt.Println("Error in business logic:", err)
                }
            case <-ctx.Done():
                return
            }
        }
    }()

    // 模拟程序运行一段时间后停止定时任务
    time.Sleep(60 * time.Second)
    cancel()
    fmt.Println("定时任务已停止")
}

优雅停止定时任务的解释

  1. 创建 context.Context:通过 context.WithCancel(context.Background()) 创建一个可取消的 context.Contextctx 用于控制 Goroutine 的生命周期,cancel 函数用于取消 ctx
  2. select 中监听 ctx.Done():在 Goroutine 内部的 select 语句中,除了监听 ticker.C 通道外,还监听 ctx.Done() 通道。当 ctx.Done() 通道接收到信号时,意味着 context.Context 被取消,此时 Goroutine 应该停止执行,通过 return 语句退出 Goroutine
  3. 调用 cancel 函数:在需要停止定时任务的地方,调用 cancel 函数,这会向 ctx.Done() 通道发送信号,通知 Goroutine 停止执行,从而实现优雅停止定时任务。同时,time.Ticker 也通过 defer ticker.Stop() 停止,避免资源泄漏。