面试题答案
一键面试接口定义
type Worker interface {
DoWork()
}
这个接口定义非常简单,只包含一个 DoWork
方法,用于执行核心业务逻辑。因为接口本身不涉及具体实现,所以不直接处理边界情况,它只是提供了一个统一的方法签名,所有实现该接口的结构体都必须实现 DoWork
方法。
结构体实现
type MyWorker struct {
mu sync.Mutex
// 其他可能需要的业务数据字段
}
func (w *MyWorker) DoWork() {
w.mu.Lock()
defer w.mu.Unlock()
// 核心业务逻辑代码
}
这里定义了 MyWorker
结构体,包含一个 sync.Mutex
类型的字段 mu
用于并发控制。在 DoWork
方法实现中,通过 Lock
和 Unlock
方法确保同一时间只有一个协程可以进入核心业务逻辑部分,从而避免资源竞争。如果有多个协程同时调用 DoWork
方法,只有获取到锁的协程能执行,其他协程会等待锁的释放。这样就处理了资源竞争的边界情况。
并发控制机制
func main() {
var w Worker = &MyWorker{}
var wg sync.WaitGroup
numRoutines := 10
for i := 0; i < numRoutines; i++ {
wg.Add(1)
go func() {
defer wg.Done()
w.DoWork()
}()
}
wg.Wait()
}
在 main
函数中,创建了一个实现了 Worker
接口的 MyWorker
实例,并使用 sync.WaitGroup
来等待所有协程完成。每个协程在启动时调用 wg.Add(1)
表示任务开始,在结束时调用 wg.Done()
表示任务完成。wg.Wait()
会阻塞主线程,直到所有协程都调用了 wg.Done()
。这种机制确保了所有协程都能正确完成 DoWork
方法的执行,避免了协程提前结束或未完全执行完业务逻辑的情况,从整体上保障了高并发场景下程序的正确性。同时结合 MyWorker
结构体中 sync.Mutex
的使用,进一步确保了每个协程执行 DoWork
方法时不会出现资源竞争等边界问题。对于死锁问题,在设计时要保证锁的获取和释放逻辑正确,避免出现循环依赖获取锁的情况,这里由于只有一个锁且获取和释放逻辑简单清晰,所以不容易出现死锁情况。如果业务更复杂涉及多个锁,要按照一定顺序获取锁,避免形成死锁环。