MST

星途 面试题库

面试题:Go语言Goroutine并发模型中的竞争条件处理

假设你有一个银行转账的场景,在Go语言中使用Goroutine模拟两个账户之间的转账操作。一个账户向另一个账户转账一定金额,由于是并发操作,可能会出现竞争条件。请说明如何使用互斥锁(Mutex)或通道(Channel)来避免竞争条件,并且编写代码实现转账逻辑。
13.9万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

使用互斥锁(Mutex)

  1. 原理:互斥锁用于保证在同一时间只有一个Goroutine能够访问共享资源,从而避免竞争条件。在银行转账场景中,账户余额就是共享资源。
  2. 代码实现
package main

import (
    "fmt"
    "sync"
)

// Account 定义账户结构体
type Account struct {
    balance int
    mutex   sync.Mutex
}

// Withdraw 从账户中取款
func (a *Account) Withdraw(amount int) {
    a.mutex.Lock()
    defer a.mutex.Unlock()
    if a.balance >= amount {
        a.balance -= amount
    }
}

// Deposit 向账户中存款
func (a *Account) Deposit(amount int) {
    a.mutex.Lock()
    defer a.mutex.Unlock()
    a.balance += amount
}

func transfer(from, to *Account, amount int, wg *sync.WaitGroup) {
    defer wg.Done()
    from.Withdraw(amount)
    to.Deposit(amount)
}

func main() {
    var wg sync.WaitGroup
    account1 := &Account{balance: 1000}
    account2 := &Account{balance: 2000}

    // 模拟转账
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go transfer(account1, account2, 100, &wg)
    }

    wg.Wait()
    fmt.Printf("Account1 balance: %d\n", account1.balance)
    fmt.Printf("Account2 balance: %d\n", account2.balance)
}

使用通道(Channel)

  1. 原理:通道可以用于在Goroutine之间传递数据,并且保证数据传递的顺序性和原子性。我们可以通过通道来控制对账户余额的访问,从而避免竞争条件。
  2. 代码实现
package main

import (
    "fmt"
    "sync"
)

// Account 定义账户结构体
type Account struct {
    balance int
    ch      chan int
}

// Withdraw 从账户中取款
func (a *Account) Withdraw(amount int) {
    a.ch <- 1
    if a.balance >= amount {
        a.balance -= amount
    }
    <-a.ch
}

// Deposit 向账户中存款
func (a *Account) Deposit(amount int) {
    a.ch <- 1
    a.balance += amount
    <-a.ch
}

func transfer(from, to *Account, amount int, wg *sync.WaitGroup) {
    defer wg.Done()
    from.Withdraw(amount)
    to.Deposit(amount)
}

func main() {
    var wg sync.WaitGroup
    account1 := &Account{balance: 1000, ch: make(chan int, 1)}
    account2 := &Account{balance: 2000, ch: make(chan int, 1)}

    // 模拟转账
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go transfer(account1, account2, 100, &wg)
    }

    wg.Wait()
    fmt.Printf("Account1 balance: %d\n", account1.balance)
    fmt.Printf("Account2 balance: %d\n", account2.balance)
}