面试题答案
一键面试package main
import (
"fmt"
"sync"
)
// Account 结构体表示账户
type Account struct {
Balance int
mu sync.Mutex
}
// NewAccount 创建一个新账户并返回包含操作闭包的结构体
func NewAccount(initialBalance int) struct {
Deposit func(int)
Withdraw func(int)
GetBalance func() int
} {
account := Account{
Balance: initialBalance,
}
deposit := func(amount int) {
account.mu.Lock()
defer account.mu.Unlock()
account.Balance += amount
}
withdraw := func(amount int) {
account.mu.Lock()
defer account.mu.Unlock()
if account.Balance >= amount {
account.Balance -= amount
} else {
fmt.Println("余额不足")
}
}
getBalance := func() int {
account.mu.Lock()
defer account.mu.Unlock()
return account.Balance
}
return struct {
Deposit func(int)
Withdraw func(int)
GetBalance func() int
}{
Deposit: deposit,
Withdraw: withdraw,
GetBalance: getBalance,
}
}
func main() {
var wg sync.WaitGroup
account := NewAccount(100)
// 模拟并发存款
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
account.Deposit(10)
}()
}
// 模拟并发取款
for i := 0; i < 5; i++ {
wg.Add(1)
go func() {
defer wg.Done()
account.Withdraw(20)
}()
}
wg.Wait()
fmt.Println("最终余额:", account.GetBalance())
}
代码解释
- Account 结构体:定义了
Account
结构体,包含Balance
字段用于表示账户余额,以及一个sync.Mutex
类型的mu
字段用于互斥锁,以保护对Balance
的并发访问。 - NewAccount 函数:此函数接受一个初始余额作为参数,返回一个包含存款
Deposit
、取款Withdraw
和获取余额GetBalance
操作的闭包的结构体。- Deposit 闭包:使用
account.mu.Lock()
锁定互斥锁,确保在存款操作执行期间,其他协程无法访问Balance
字段。操作完成后,使用defer account.mu.Unlock()
解锁互斥锁。存款操作只是简单地将传入的金额加到Balance
上。 - Withdraw 闭包:同样先锁定互斥锁,检查余额是否足够。如果足够则减去取款金额,否则打印余额不足的提示。最后解锁互斥锁。
- GetBalance 闭包:锁定互斥锁,返回当前的
Balance
值,然后解锁互斥锁。
- Deposit 闭包:使用
- main 函数:创建一个初始余额为 100 的账户,并模拟多个协程并发执行存款和取款操作。使用
sync.WaitGroup
等待所有协程完成操作,最后打印最终的账户余额。
竞态条件处理
在这个实现中,通过使用 sync.Mutex
互斥锁来处理竞态条件。在对 Balance
字段进行任何读或写操作之前,先锁定互斥锁,操作完成后解锁。这样可以确保在同一时间只有一个协程能够访问和修改 Balance
字段,从而保证了数据的一致性和安全性。如果不使用互斥锁,多个协程同时读写 Balance
字段可能会导致数据不一致的问题,例如丢失更新或者读取到脏数据。