面试题答案
一键面试设计思路
- 任务队列:使用Go语言的通道(channel)来实现任务队列,通道本身是线程安全的,能够有效避免数据竞争问题。
- 任务处理逻辑:通过一个映射(map)来存储不同类型任务的处理函数,实现动态扩展任务处理逻辑。当有新的任务类型时,只需在映射中注册对应的处理函数即可。
- 空接口性能瓶颈及优化:
- 性能瓶颈:空接口类型断言在运行时进行,会带来额外的开销。并且空接口会导致数据在内存中的存储和访问模式变得复杂,影响缓存命中率。
- 优化:尽量减少类型断言的次数,例如可以在任务进入队列前就进行类型断言,或者使用类型开关(type switch)来批量处理不同类型的任务,提高代码执行效率。同时,考虑使用具体类型的通道和处理函数,避免过多使用空接口。
关键代码示例
package main
import (
"fmt"
)
// Task 定义任务接口
type Task interface {
Execute()
}
// TaskDispatcher 任务分发器
type TaskDispatcher struct {
taskQueue chan Task
handlers map[interface{}]func(Task)
}
// NewTaskDispatcher 创建新的任务分发器
func NewTaskDispatcher(queueSize int) *TaskDispatcher {
return &TaskDispatcher{
taskQueue: make(chan Task, queueSize),
handlers: make(map[interface{}]func(Task)),
}
}
// RegisterHandler 注册任务处理函数
func (td *TaskDispatcher) RegisterHandler(taskType interface{}, handler func(Task)) {
td.handlers[taskType] = handler
}
// SubmitTask 提交任务到队列
func (td *TaskDispatcher) SubmitTask(task Task) {
td.taskQueue <- task
}
// Start 启动任务分发器
func (td *TaskDispatcher) Start() {
for task := range td.taskQueue {
taskType := fmt.Sprintf("%T", task)
if handler, ok := td.handlers[taskType]; ok {
handler(task)
} else {
fmt.Printf("No handler for task type %T\n", task)
}
}
}
// 示例任务1
type ExampleTask1 struct{}
func (et *ExampleTask1) Execute() {
fmt.Println("Executing ExampleTask1")
}
// 示例任务2
type ExampleTask2 struct{}
func (et *ExampleTask2) Execute() {
fmt.Println("Executing ExampleTask2")
}
func main() {
dispatcher := NewTaskDispatcher(10)
// 注册任务处理函数
dispatcher.RegisterHandler(fmt.Sprintf("%T", &ExampleTask1{}), func(task Task) {
task.Execute()
})
dispatcher.RegisterHandler(fmt.Sprintf("%T", &ExampleTask2{}), func(task Task) {
task.Execute()
})
// 提交任务
dispatcher.SubmitTask(&ExampleTask1{})
dispatcher.SubmitTask(&ExampleTask2{})
// 启动任务分发器
go dispatcher.Start()
// 防止主函数退出
select {}
}
在上述代码中:
Task
接口定义了任务的执行方法Execute
。TaskDispatcher
结构体包含任务队列taskQueue
和任务处理函数映射handlers
。NewTaskDispatcher
函数创建一个新的任务分发器实例。RegisterHandler
函数用于注册不同类型任务的处理函数。SubmitTask
函数将任务提交到任务队列。Start
函数启动任务分发器,从任务队列中取出任务并调用对应的处理函数。- 定义了两个示例任务
ExampleTask1
和ExampleTask2
,并在main
函数中注册处理函数、提交任务并启动任务分发器。