MST
星途 面试题库

面试题:Go语言Mutex在复杂并发场景下的应用

假设有一个Go语言编写的银行转账系统,涉及多个账户之间的资金转移操作,每个账户余额是共享资源。请描述如何利用Mutex确保在高并发的转账请求下,账户余额的增减操作不会出现数据竞争问题,并且考虑如何避免死锁情况的发生,给出具体的代码实现思路。
34.2万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试
  1. 利用Mutex确保无数据竞争
    • 为每个账户结构体添加一个sync.Mutex字段。当进行账户余额的增减操作时,先锁定对应的Mutex,操作完成后再解锁。这样在同一时间只有一个协程可以操作账户余额,避免数据竞争。
  2. 避免死锁
    • 固定锁的获取顺序:如果转账操作涉及多个账户,为所有账户定义一个固定的顺序(比如按账户ID从小到大)。在进行转账时,所有协程都按照这个固定顺序获取锁。这样可以防止循环获取锁导致的死锁。
    • 超时处理:在获取锁时使用TryLock或设置获取锁的超时时间。如果在规定时间内没有获取到锁,放弃当前操作并进行相应处理(比如重试或返回错误)。

以下是代码实现思路示例:

package main

import (
    "fmt"
    "sync"
)

// Account 定义账户结构体
type Account struct {
    ID     int
    Balance int
    mu     sync.Mutex
}

// Transfer 转账函数
func Transfer(from, to *Account, amount int, wg *sync.WaitGroup) {
    defer wg.Done()
    // 固定锁的获取顺序,假设按账户ID从小到大
    var first, second *Account
    if from.ID < to.ID {
        first, second = from, to
    } else {
        first, second = to, from
    }
    first.mu.Lock()
    defer first.mu.Unlock()
    second.mu.Lock()
    defer second.mu.Unlock()

    if from.Balance >= amount {
        from.Balance -= amount
        to.Balance += amount
    } else {
        fmt.Println("Insufficient balance in account", from.ID)
    }
}

func main() {
    var wg sync.WaitGroup
    account1 := Account{ID: 1, Balance: 1000}
    account2 := Account{ID: 2, Balance: 500}

    // 模拟多个转账请求
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go Transfer(&account1, &account2, 100, &wg)
    }
    wg.Wait()

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

在上述代码中:

  • Account结构体包含账户ID、余额和一个Mutex
  • Transfer函数在进行转账操作前,按账户ID的顺序获取锁,防止死锁。在获取锁后进行余额的增减操作,通过Mutex保证数据的一致性,避免数据竞争。
  • main函数模拟了多个并发的转账请求。