MST

星途 面试题库

面试题:Go语言函数调用与并发编程中的规约应用

在Go语言并发编程中,使用多个goroutine调用函数时,会涉及到函数调用规约相关的哪些问题?例如数据竞争、资源共享等。请设计一个场景,在该场景下通过合理的函数调用规约(如使用sync包中的工具)来避免并发带来的问题,并实现预期的功能。
18.2万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

1. 函数调用规约相关问题

  • 数据竞争:当多个goroutine同时读写共享数据时,可能会导致数据竞争问题。例如一个goroutine在读取数据时,另一个goroutine同时在修改该数据,这会导致读取到的数据不一致或者是无效数据。
  • 资源共享:多个goroutine可能会共享一些资源,如文件描述符、数据库连接等。如果没有正确的管理,可能会导致资源的重复使用、泄漏等问题。

2. 设计场景及实现

假设我们有一个银行账户,多个goroutine可能会同时对这个账户进行存款和取款操作。为了避免并发带来的问题,我们可以使用sync.Mutex来保证同一时间只有一个goroutine能够对账户余额进行操作。

package main

import (
    "fmt"
    "sync"
)

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

// Deposit 存款操作
func (b *BankAccount) Deposit(amount int) {
    b.mutex.Lock()
    defer b.mutex.Unlock()
    b.balance += amount
}

// Withdraw 取款操作
func (b *BankAccount) Withdraw(amount int) bool {
    b.mutex.Lock()
    defer b.mutex.Unlock()
    if b.balance >= amount {
        b.balance -= amount
        return true
    }
    return false
}

// GetBalance 获取账户余额
func (b *BankAccount) GetBalance() int {
    b.mutex.Lock()
    defer b.mutex.Unlock()
    return b.balance
}

func main() {
    var wg sync.WaitGroup
    account := BankAccount{balance: 100}

    // 模拟多个goroutine进行存款和取款操作
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            account.Deposit(10)
        }()
    }

    for i := 0; i < 3; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            account.Withdraw(20)
        }()
    }

    wg.Wait()
    fmt.Println("Final balance:", account.GetBalance())
}

在上述代码中:

  • BankAccount结构体包含一个balance字段表示账户余额,以及一个sync.Mutex类型的mutex字段用于同步访问。
  • Deposit方法在修改balance之前先锁定mutex,操作完成后解锁,确保同一时间只有一个goroutine能执行存款操作。
  • Withdraw方法类似,先锁定mutex,检查余额是否足够,然后进行取款操作并解锁。
  • GetBalance方法同样通过锁定和解锁mutex来安全地获取账户余额。

通过这种方式,使用sync.Mutex来遵循函数调用规约,有效地避免了并发操作带来的数据竞争和资源共享问题,保证了银行账户操作的正确性。