MST

星途 面试题库

面试题:Go接口回调在并发场景下的应用

在Go语言并发编程中,使用接口回调机制实现一个场景:有多个 `Task` 结构体,每个 `Task` 有一个执行方法 `Run` ,同时有一个 `Scheduler` 结构体用于调度这些 `Task` 。请利用接口回调机制,在 `Scheduler` 中实现一种调度策略,当某个 `Task` 执行完成后,回调一个通知函数,告知 `Scheduler` 该任务已完成,且需要处理并发安全问题。
12.1万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试
package main

import (
    "fmt"
    "sync"
)

// Task 接口定义任务的执行方法
type Task interface {
    Run()
}

// TaskImpl 具体任务实现
type TaskImpl struct {
    id int
}

func (t *TaskImpl) Run() {
    fmt.Printf("Task %d is running\n", t.id)
}

// NotifyFunc 回调通知函数类型
type NotifyFunc func(task Task)

// Scheduler 调度器结构体
type Scheduler struct {
    tasks    []Task
    notify   NotifyFunc
    wg       sync.WaitGroup
    mutex    sync.Mutex
}

// AddTask 添加任务到调度器
func (s *Scheduler) AddTask(task Task) {
    s.mutex.Lock()
    s.tasks = append(s.tasks, task)
    s.mutex.Unlock()
}

// SetNotify 设置回调通知函数
func (s *Scheduler) SetNotify(f NotifyFunc) {
    s.notify = f
}

// Start 开始调度任务
func (s *Scheduler) Start() {
    for _, task := range s.tasks {
        s.wg.Add(1)
        go func(t Task) {
            defer s.wg.Done()
            t.Run()
            if s.notify != nil {
                s.notify(t)
            }
        }(task)
    }
    s.wg.Wait()
}

func main() {
    scheduler := &Scheduler{}

    task1 := &TaskImpl{id: 1}
    task2 := &TaskImpl{id: 2}

    scheduler.AddTask(task1)
    scheduler.AddTask(task2)

    scheduler.SetNotify(func(task Task) {
        fmt.Printf("Task %v has completed\n", task)
    })

    scheduler.Start()
}
  1. 定义 Task 接口,包含 Run 方法,所有任务都需要实现这个接口。
  2. 定义 TaskImpl 结构体,实现 Task 接口的 Run 方法,这里简单打印任务运行信息。
  3. 定义 NotifyFunc 类型,作为回调通知函数。
  4. 定义 Scheduler 结构体,包含任务列表 tasks、回调通知函数 notify、等待组 wg 和互斥锁 mutex 来处理并发安全问题。
  5. AddTask 方法用于添加任务到调度器,使用互斥锁保证并发安全。
  6. SetNotify 方法设置回调通知函数。
  7. Start 方法启动调度器,遍历任务列表,并发执行每个任务,任务执行完成后调用回调通知函数。
  8. main 函数中创建调度器、任务,并设置回调函数,最后启动调度器。