乐闻世界logo
搜索文章和话题

Go 如何处理对共享数据的并发访问?

1 个月前提问
1 个月前修改
浏览次数8

1个答案

1

在Go中,处理对共享数据的并发访问主要有两种常用方法:使用互斥锁(Mutex)和使用通道(Channel)。下面,我会详细介绍这两种方法,并提供示例。

1. 使用互斥锁(Mutex)

互斥锁是一种同步机制,用来避免多个goroutine在同一时间内访问共享数据。Go标准库中的sync包提供了Mutex类型来支持这种需求。

示例:

假设我们有一个共享的账户余额,多个goroutine试图同时更新这个余额。

go
package main import ( "fmt" "sync" "time" ) type Account struct { balance int mu sync.Mutex } func (a *Account) Deposit(amount int) { a.mu.Lock() // 加锁 defer a.mu.Unlock() // 使用defer确保解锁 a.balance += amount } func main() { var wg sync.WaitGroup account := &Account{balance: 100} // 模拟多个goroutine同时存款 for i := 0; i < 5; i++ { wg.Add(1) go func(amount int) { defer wg.Done() account.Deposit(amount) }(100) } wg.Wait() fmt.Println("Final balance:", account.balance) }

在这个例子中,我们使用了Mutex来控制对balance的访问,确保每次只有一个goroutine可以修改余额。

2. 使用通道(Channel)

通道是Go中的一个核心特性,用于在goroutines之间传递消息。通过使用通道,我们可以避免显式的使用锁,从而用更“Go式”的方式来处理并发。

示例:

我们可以创建一个专门用于更新账户余额的goroutine,并通过通道接收更新指令。

go
package main import ( "fmt" "sync" ) type operation struct { amount int balance chan int } func main() { var wg sync.WaitGroup opChan := make(chan operation, 5) balance := 100 // 开启一个goroutine来管理余额 go func() { for op := range opChan { balance += op.amount op.balance <- balance } }() // 发送操作到goroutine for i := 0; i < 5; i++ { wg.Add(1) go func(amount int) { defer wg.Done() balanceChan := make(chan int) opChan <- operation{amount, balanceChan} newBalance := <-balanceChan fmt.Println("New balance:", newBalance) }(100) } wg.Wait() close(opChan) // 关闭通道,结束余额管理goroutine }

在这个例子中,我们创建了一个操作类型,包含金额和一个返回新余额的通道。一个单独的goroutine负责监听这个通道,处理所有的余额更新,并通过另一个通道返回新的余额。这样,我们就避免了对共享资源的直接并发访问。

总结

在Go中处理对共享数据的并发访问时,建议根据具体情况选择合适的同步机制。对于简单的数据保护,互斥锁是一个好的选择。而当涉及到复杂的状态或多个资源的协调时,通道配合goroutine可以提供更高的灵活性和更好的扩展性。

2024年8月7日 18:15 回复

你的答案