面试题答案
一键面试设计思路
- 任务队列与调度器:创建一个任务队列,将所有依赖闭包捕获外部变量的任务添加到队列中。调度器负责从队列中取出任务并安排执行。
- 资源限制处理:设置最大并发数,避免过多任务同时执行导致系统资源耗尽。可以使用信号量(Semaphore)来控制并发任务的数量。
- 闭包特性利用:闭包可以捕获并保存外部变量的状态,使得任务在执行时能够访问到正确的变量值。同时,由于闭包的存在,任务可以被独立封装,便于调度和管理。
- 任务依赖处理:对于有依赖关系的任务,可以使用任务图(如拓扑排序)来确定执行顺序。在调度时,确保依赖的任务先完成。
- 资源竞争处理:使用锁(Mutex)来保护共享资源,避免多个任务同时访问修改共享数据导致的数据不一致问题。
关键代码示例(以Go语言为例)
package main
import (
"fmt"
"sync"
"time"
)
// 任务结构体
type Task struct {
id int
execute func()
depends []int
complete chan struct{}
}
// 任务调度器
type Scheduler struct {
tasks map[int]Task
maxConcur int
semaphore chan struct{}
mutex sync.Mutex
dependencies map[int][]int
}
// 添加任务
func (s *Scheduler) AddTask(task Task) {
s.mutex.Lock()
s.tasks[task.id] = task
s.dependencies[task.id] = task.depends
s.mutex.Unlock()
}
// 执行任务
func (s *Scheduler) Execute() {
var wg sync.WaitGroup
for {
var task Task
s.mutex.Lock()
for id, t := range s.tasks {
ready := true
for _, dep := range s.dependencies[id] {
if _, exists := s.tasks[dep]; exists {
ready = false
break
}
}
if ready {
task = t
delete(s.tasks, id)
delete(s.dependencies, id)
break
}
}
s.mutex.Unlock()
if task.id == 0 {
break
}
s.semaphore <- struct{}{}
wg.Add(1)
go func(t Task) {
defer func() { <-s.semaphore; wg.Done() }()
t.execute()
close(t.complete)
}(task)
}
wg.Wait()
}
func main() {
scheduler := Scheduler{
tasks: make(map[int]Task),
maxConcur: 3,
semaphore: make(chan struct{}, 3),
dependencies: make(map[int][]int),
}
var sharedVar int
var sharedMutex sync.Mutex
task1 := Task{
id: 1,
execute: func() {
sharedMutex.Lock()
sharedVar++
fmt.Printf("Task 1 executed, sharedVar: %d\n", sharedVar)
sharedMutex.Unlock()
time.Sleep(2 * time.Second)
},
depends: []int{},
complete: make(chan struct{}),
}
task2 := Task{
id: 2,
execute: func() {
sharedMutex.Lock()
sharedVar += 2
fmt.Printf("Task 2 executed, sharedVar: %d\n", sharedVar)
sharedMutex.Unlock()
time.Sleep(1 * time.Second)
},
depends: []int{1},
complete: make(chan struct{}),
}
scheduler.AddTask(task1)
scheduler.AddTask(task2)
scheduler.Execute()
}
在上述代码中:
Task
结构体定义了任务的ID、执行函数、依赖任务ID列表以及完成信号通道。Scheduler
结构体管理任务队列、最大并发数、信号量以及任务依赖关系。AddTask
方法用于将任务添加到调度器中。Execute
方法负责调度任务执行,使用信号量控制并发数,通过拓扑排序处理任务依赖,并使用互斥锁保护共享资源。
通过这种方式,可以有效地利用闭包特性,对并发任务进行调度优化,提高整体执行效率和资源利用率。