可能出现的边界情况
- 竞态条件(Race Condition):当多个 goroutine 同时访问和修改共享资源时,最终结果依赖于 goroutine 的执行顺序,导致不可预测的行为。例如,多个 goroutine 同时对一个共享变量进行读写操作。
- 数据竞争(Data Race):这是竞态条件的一种特殊情况,指的是多个 goroutine 同时读写共享内存,且至少有一个是写操作,并且没有适当的同步机制。
使用同步原语应对边界情况
- sync.Mutex:互斥锁,用于保证在同一时间只有一个 goroutine 可以访问共享资源。
- sync.RWMutex:读写互斥锁,允许多个 goroutine 同时读共享资源,但只允许一个 goroutine 写。当有写操作时,所有读操作都被阻塞。
代码示例
package main
import (
"fmt"
"sync"
)
// Counter 结构体包含一个计数器和一个互斥锁
type Counter struct {
count int
mu sync.Mutex
}
// Increment 方法用于增加计数器的值
func (c *Counter) Increment() {
c.mu.Lock()
c.count++
c.mu.Unlock()
}
// GetCount 方法用于获取计数器的值
func (c *Counter) GetCount() int {
c.mu.Lock()
defer c.mu.Unlock()
return c.count
}
func main() {
var wg sync.WaitGroup
counter := Counter{}
// 启动10个goroutine来增加计数器的值
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
counter.Increment()
}()
}
// 等待所有goroutine完成
wg.Wait()
// 获取并打印计数器的值
fmt.Println("Final count:", counter.GetCount())
}
代码解释
- Counter 结构体:包含一个
count
变量用于计数,以及一个 sync.Mutex
类型的 mu
互斥锁。
- Increment 方法:在增加
count
值之前,通过 c.mu.Lock()
锁定互斥锁,确保同一时间只有一个 goroutine 可以修改 count
。修改完成后,通过 c.mu.Unlock()
解锁互斥锁。
- GetCount 方法:在读取
count
值之前,同样锁定互斥锁,防止在读取过程中有其他 goroutine 修改 count
。使用 defer c.mu.Unlock()
确保无论函数如何返回,互斥锁都会被解锁。
- main 函数:启动10个 goroutine 并发调用
Increment
方法,然后等待所有 goroutine 完成,最后获取并打印计数器的值。通过这种方式,确保了在并发环境下 count
的正确更新和读取,避免了竞态条件。