面试题答案
一键面试通过通道实现计数器正确更新
package main
import (
"fmt"
)
func main() {
counter := 0
updates := make(chan int)
done := make(chan struct{})
// 模拟多个协程进行加1操作
for i := 0; i < 5; i++ {
go func() {
updates <- 1
}()
}
go func() {
for u := range updates {
counter += u
}
done <- struct{}{}
}()
// 等待所有更新完成
for i := 0; i < 5; i++ {
updates <- 0
}
close(updates)
<-done
fmt.Println("Final counter value:", counter)
}
在上述代码中:
- 我们创建了一个
updates
通道用于接收协程发来的加1信号。 - 多个协程通过向
updates
通道发送1
来表示进行加1操作。 - 有一个专门的协程从
updates
通道接收数据并更新计数器counter
。 - 为了确保所有协程的更新操作完成,我们通过向
updates
通道发送0
并关闭通道来通知接收协程结束。
优势
- 避免死锁:使用锁机制时,如果多个协程对多个资源进行加锁,可能会因为加锁顺序不一致等原因导致死锁。而通道基于消息传递,不存在死锁问题。
- 更符合Go语言的设计理念:Go语言提倡通过通信来共享内存,而不是共享内存来通信。使用通道的方式更符合这种设计哲学,代码结构更清晰。
劣势
- 性能问题:通道操作本身涉及到通信和同步开销。在高并发场景下,频繁的通道操作可能比锁机制有更高的性能损耗,特别是在简单的计数器更新场景中,锁机制可能更加直接高效。
- 实现复杂:相比于直接使用锁,通过通道实现计数器更新的代码结构更复杂,需要更多的设计和理解成本,尤其是在处理更复杂的同步需求时。