面试题答案
一键面试- 使用
sync.Mutex
保护信号量状态- 定义一个结构体来表示信号量,其中包含信号量的当前值和一个互斥锁。
type Semaphore struct { value int mutex sync.Mutex }
- 在获取(
Acquire
)和释放(Release
)信号量的方法中,使用互斥锁来保护对信号量值的修改。
func (s *Semaphore) Acquire() { s.mutex.Lock() for s.value <= 0 { s.mutex.Unlock() runtime.Gosched() s.mutex.Lock() } s.value-- s.mutex.Unlock() } func (s *Semaphore) Release() { s.mutex.Lock() s.value++ s.mutex.Unlock() }
- 避免死锁
- 合理的锁顺序:在涉及多个信号量或锁的情况下,确保所有协程以相同的顺序获取锁。例如,如果有两个信号量
sema1
和sema2
,所有协程都应该先获取sema1
,再获取sema2
。 - 超时机制:在获取信号量时设置超时。可以使用
context.Context
来实现这一点。
func (s *Semaphore) AcquireWithTimeout(ctx context.Context) bool { s.mutex.Lock() for s.value <= 0 { s.mutex.Unlock() select { case <-ctx.Done(): return false default: runtime.Gosched() } s.mutex.Lock() } s.value-- s.mutex.Unlock() return true }
- 合理的锁顺序:在涉及多个信号量或锁的情况下,确保所有协程以相同的顺序获取锁。例如,如果有两个信号量
- 防止资源泄漏
- 确保释放:在获取信号量后,使用Go语言的
defer
语句确保无论获取信号量后的代码路径如何,信号量都会被正确释放。
func someFunction(s *Semaphore) { s.Acquire() defer s.Release() // 访问共享资源的代码 }
- 异常处理:如果在获取信号量后发生异常,要保证信号量能够被正确释放。Go语言中虽然没有传统的异常处理机制,但可以通过
recover
来处理panic
,并在defer
中释放信号量。
func someFunctionWithPanic(s *Semaphore) { s.Acquire() defer func() { if r := recover(); r != nil { s.Release() panic(r) } }() // 可能发生panic的代码 }
- 确保释放:在获取信号量后,使用Go语言的