MST

星途 面试题库

面试题:Go并发编程中如何测试带竞争条件的场景

假设有一个银行账户转账的场景,多个goroutine同时进行转账操作。请描述如何使用Go的工具来检测这个场景中的竞争条件,并编写测试代码进行验证。若发现竞争条件,应如何修改代码避免竞争?
29.5万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

检测竞争条件

在Go中,可以使用-race标志来检测竞争条件。-race标志会启用Go的竞态检测器,它会记录所有的内存访问操作,并在运行时检查是否存在数据竞争。

编写测试代码

假设我们有如下简单的银行账户转账代码:

package main

import (
    "fmt"
)

var balance int

func transfer(from, to *int, amount int) {
    *from = *from - amount
    *to = *to + amount
}

func main() {
    account1 := 100
    account2 := 200

    go transfer(&account1, &account2, 50)
    go transfer(&account2, &account1, 30)

    fmt.Println("Account 1:", account1)
    fmt.Println("Account 2:", account2)
}

要测试这个代码是否存在竞争条件,可以将上述代码保存为main.go,然后使用以下命令运行:

go run -race main.go

如果存在竞争条件,竞态检测器会输出相关信息,指出竞争发生的位置。

修改代码避免竞争

为了避免竞争条件,可以使用sync.Mutex来保护共享资源。修改后的代码如下:

package main

import (
    "fmt"
    "sync"
)

var (
    balance int
    mu      sync.Mutex
)

func transfer(from, to *int, amount int, wg *sync.WaitGroup) {
    defer wg.Done()
    mu.Lock()
    *from = *from - amount
    *to = *to + amount
    mu.Unlock()
}

func main() {
    var wg sync.WaitGroup
    account1 := 100
    account2 := 200

    wg.Add(2)
    go transfer(&account1, &account2, 50, &wg)
    go transfer(&account2, &account1, 30, &wg)

    wg.Wait()
    fmt.Println("Account 1:", account1)
    fmt.Println("Account 2:", account2)
}

在上述代码中,通过sync.MutexLockUnlock方法来确保在同一时间只有一个goroutine可以访问共享资源(即账户余额),从而避免了竞争条件。