面试题答案
一键面试设计思路
- 任务分发:将任务封装成函数,并通过一个任务队列(可以使用
channel
实现)来接收这些任务。 - 任务执行:启动多个
goroutine
来从任务队列中取出任务并执行。每个goroutine
作为一个工作者(worker),负责处理单个任务。 - 结果收集:每个任务执行完成后,将结果发送到另一个
channel
,主程序从这个结果channel
中收集所有任务的执行结果。 - 性能优化:
- 避免资源竞争:使用
channel
作为通信机制,因为channel
本身是线程安全的,通过它来传递任务和结果可以有效避免资源竞争。 - 合理利用
goroutine
:根据系统的CPU核心数和可用内存,合理设置goroutine
的数量。过多的goroutine
可能导致上下文切换开销增大,过少则无法充分利用系统资源。 - 缓冲
channel
:在任务队列和结果收集channel
上使用适当的缓冲区,可以减少阻塞,提高并发性能。
- 避免资源竞争:使用
核心代码片段
package main
import (
"fmt"
)
// Task 定义任务类型,是一个返回结果的函数
type Task func() interface{}
// Worker 定义工作者函数,从任务队列中取任务并执行,将结果发送到结果队列
func Worker(taskQueue <-chan Task, resultQueue chan<- interface{}) {
for task := range taskQueue {
result := task()
resultQueue <- result
}
}
// RunTasks 启动任务处理系统,分发任务并收集结果
func RunTasks(tasks []Task, workerCount int) []interface{} {
taskQueue := make(chan Task, len(tasks))
resultQueue := make(chan interface{}, len(tasks))
// 启动工作者
for i := 0; i < workerCount; i++ {
go Worker(taskQueue, resultQueue)
}
// 分发任务
for _, task := range tasks {
taskQueue <- task
}
close(taskQueue)
// 收集结果
var results []interface{}
for i := 0; i < len(tasks); i++ {
results = append(results, <-resultQueue)
}
close(resultQueue)
return results
}
func main() {
// 示例任务
tasks := []Task{
func() interface{} { return "Task 1 result" },
func() interface{} { return "Task 2 result" },
func() interface{} { return "Task 3 result" },
}
// 运行任务
results := RunTasks(tasks, 2)
fmt.Println(results)
}
在上述代码中:
Task
是一个函数类型,代表单个任务。Worker
函数是一个goroutine
,从taskQueue
中取出任务并执行,将结果发送到resultQueue
。RunTasks
函数负责初始化任务队列、结果队列,启动工作者goroutine
,分发任务并收集结果。- 在
main
函数中,创建了一些示例任务并调用RunTasks
函数执行任务并获取结果。