MST

星途 面试题库

面试题:Go类型方法在并发场景下的边界情况及应对策略

在Go语言的并发编程中,一个类型的方法可能会被多个goroutine同时调用。请描述在这种并发场景下,可能出现的边界情况,比如竞态条件等。并且阐述如何通过使用Go的同步原语(如 `sync.Mutex`、`sync.RWMutex` 等)或其他机制来正确应对这些边界情况。请给出完整的代码示例并解释。
10.7万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

可能出现的边界情况

  1. 竞态条件(Race Condition):当多个 goroutine 同时访问和修改共享资源时,最终结果依赖于 goroutine 的执行顺序,导致不可预测的行为。例如,多个 goroutine 同时对一个共享变量进行读写操作。
  2. 数据竞争(Data Race):这是竞态条件的一种特殊情况,指的是多个 goroutine 同时读写共享内存,且至少有一个是写操作,并且没有适当的同步机制。

使用同步原语应对边界情况

  1. sync.Mutex:互斥锁,用于保证在同一时间只有一个 goroutine 可以访问共享资源。
  2. sync.RWMutex:读写互斥锁,允许多个 goroutine 同时读共享资源,但只允许一个 goroutine 写。当有写操作时,所有读操作都被阻塞。

代码示例

package main

import (
    "fmt"
    "sync"
)

// Counter 结构体包含一个计数器和一个互斥锁
type Counter struct {
    count int
    mu    sync.Mutex
}

// Increment 方法用于增加计数器的值
func (c *Counter) Increment() {
    c.mu.Lock()
    c.count++
    c.mu.Unlock()
}

// GetCount 方法用于获取计数器的值
func (c *Counter) GetCount() int {
    c.mu.Lock()
    defer c.mu.Unlock()
    return c.count
}

func main() {
    var wg sync.WaitGroup
    counter := Counter{}

    // 启动10个goroutine来增加计数器的值
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            counter.Increment()
        }()
    }

    // 等待所有goroutine完成
    wg.Wait()

    // 获取并打印计数器的值
    fmt.Println("Final count:", counter.GetCount())
}

代码解释

  1. Counter 结构体:包含一个 count 变量用于计数,以及一个 sync.Mutex 类型的 mu 互斥锁。
  2. Increment 方法:在增加 count 值之前,通过 c.mu.Lock() 锁定互斥锁,确保同一时间只有一个 goroutine 可以修改 count。修改完成后,通过 c.mu.Unlock() 解锁互斥锁。
  3. GetCount 方法:在读取 count 值之前,同样锁定互斥锁,防止在读取过程中有其他 goroutine 修改 count。使用 defer c.mu.Unlock() 确保无论函数如何返回,互斥锁都会被解锁。
  4. main 函数:启动10个 goroutine 并发调用 Increment 方法,然后等待所有 goroutine 完成,最后获取并打印计数器的值。通过这种方式,确保了在并发环境下 count 的正确更新和读取,避免了竞态条件。