import (
"fmt"
"sync"
)
func main() {
var i int
var wg sync.WaitGroup
var mu sync.Mutex
wg.Add(2)
//协程1
go func() {
defer wg.Done()
mu.Lock()
i += 10
mu.Unlock()
}()
//协程2
go func() {
defer wg.Done()
mu.Lock()
i = 10
mu.Unlock()
}()
wg.Wait()
fmt.Printf("最终结果:%d\n", i)
}
我在测试两个协程加锁一个全局变量的影响,现在测试了50万次,输出都是20,我的理解:协程1获得了锁,i值为10,释放锁,协程2再执行,赋值10,输出为10,协程2赋值为10,释放锁,协程1获得锁,i+=10,i为20,但始终打印的值都是20,难道每次都是协程2优先获取的调度机会?
golang 多协程加锁赋值变量不可预测
- 写回答
- 好问题 0 提建议
- 关注问题
- 邀请回答
-
3条回答 默认 最新
关注让【宝码香车】来帮您解答,本回答参考DeepSeek编写,并整理提供。
如果答案让您满意,请采纳、点赞、关注,非常感谢!
.
每日一句正能量,让它带给您好心情:真正的快乐,源于内心的满足和对生活的热爱。
.根据你的代码和描述,我来分析一下为什么你测试50万次都输出20。
问题分析
你的理解基本正确,但需要补充一些关键点:
func main() { var i int var wg sync.WaitGroup var mu sync.Mutex wg.Add(2) // 协程1 go func() { defer wg.Done() mu.Lock() i += 10 // 读取i的值,然后加10 mu.Unlock() }() // 协程2 go func() { defer wg.Done() mu.Lock() i = 10 // 直接赋值为10 mu.Unlock() }() wg.Wait() fmt.Printf("最终结果:%d\n", i) }可能的结果
理论上这个程序可能有两种结果:
- 结果20:协程1先执行
- 协程1获取锁:
i += 10→i = 10 - 协程2获取锁:
i = 10→i = 10 - 最终结果:10
- 结果20:协程2先执行
- 协程2获取锁:
i = 10→i = 10 - 协程1获取锁:
i += 10→i = 20 - 最终结果:20
为什么你总是看到20
你测试50万次都输出20,这确实说明在你的测试环境中,协程2几乎总是先获得执行机会。这可能是由于:
- Goroutine调度特性:Go的调度器在某些情况下会有一定的执行顺序偏好
- 创建顺序:后创建的goroutine在某些情况下可能更容易先被调度
- 系统状态:CPU核心数、系统负载等因素可能影响调度顺序
验证方法
要看到不同的结果,可以尝试以下方法:
方法1:添加随机延迟
go func() { defer wg.Done() time.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond) mu.Lock() i += 10 mu.Unlock() }() go func() { defer wg.Done() time.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond) mu.Lock() i = 10 mu.Unlock() }()方法2:增加更多goroutine
func main() { for j := 0; j < 100; j++ { var i int var wg sync.WaitGroup var mu sync.Mutex wg.Add(2) go func() { defer wg.Done() mu.Lock() i += 10 mu.Unlock() }() go func() { defer wg.Done() mu.Lock() i = 10 mu.Unlock() }() wg.Wait() fmt.Printf("测试%d: %d\n", j+1, i) } }结论
你的代码逻辑是正确的,加锁确保了数据竞争的安全性。你总是看到结果20是因为在你的测试环境下,协程2几乎总是先获得执行机会,但这并不是绝对的。在多核CPU上或者不同的系统环境下,你可能会看到不同的结果。
关键要点:
- 加锁确保了线程安全,这是正确的
- Goroutine的执行顺序是不确定的
- 你的测试结果反映了当前环境的调度特性,但不是程序行为的唯一可能
本回答被题主选为最佳回答 , 对您是否有帮助呢?解决 无用评论 打赏 举报