MST

星途 面试题库

面试题:Go中协程的资源管理与竞争问题

假设有多个协程同时对一个共享的计数器进行加1操作,在不使用锁机制的情况下,如何通过Go语言的通道(channel)来确保计数器的正确更新,同时请说明这种方式相比于使用锁的优势和劣势。
12.7万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

通过通道实现计数器正确更新

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)
}

在上述代码中:

  1. 我们创建了一个 updates 通道用于接收协程发来的加1信号。
  2. 多个协程通过向 updates 通道发送 1 来表示进行加1操作。
  3. 有一个专门的协程从 updates 通道接收数据并更新计数器 counter
  4. 为了确保所有协程的更新操作完成,我们通过向 updates 通道发送 0 并关闭通道来通知接收协程结束。

优势

  1. 避免死锁:使用锁机制时,如果多个协程对多个资源进行加锁,可能会因为加锁顺序不一致等原因导致死锁。而通道基于消息传递,不存在死锁问题。
  2. 更符合Go语言的设计理念:Go语言提倡通过通信来共享内存,而不是共享内存来通信。使用通道的方式更符合这种设计哲学,代码结构更清晰。

劣势

  1. 性能问题:通道操作本身涉及到通信和同步开销。在高并发场景下,频繁的通道操作可能比锁机制有更高的性能损耗,特别是在简单的计数器更新场景中,锁机制可能更加直接高效。
  2. 实现复杂:相比于直接使用锁,通过通道实现计数器更新的代码结构更复杂,需要更多的设计和理解成本,尤其是在处理更复杂的同步需求时。