Golang中为什么这段代码无法运行?求解答
Golang中为什么这段代码无法运行?求解答 大家好,我叫肖恩,最近刚开始学习Go语言,但在并发代码方面遇到了一个问题:
我有两个函数在运行,但它们共享变量COUNTER,这导致了竞态条件吗?
我尝试过使用Go协程和互斥锁来锁定它,但仍然出现竞态?
你能看看下面的代码,告诉我原因吗:
package main
import (
"fmt"
"runtime"
"sync"
)
var counter int
var wg sync.WaitGroup
var mutex = sync.Mutex{}
var mutex2 = sync.Mutex{}
func main() {
fmt.Println("CPU count Start: \t", runtime.NumCPU())
fmt.Println("GoRoutines count Start: \t", runtime.NumGoroutine())
wg.Add(2)
go boo()
go bar()
wg.Wait()
fmt.Println("GoRoutines count End: \t", runtime.NumGoroutine())
fmt.Println("the end : :)")
}
func boo() int {
i := 0
for i = 0; i < 10; i++ {
mutex.Lock()
counter++
fmt.Println("count of boo: \t", counter)
mutex.Unlock()
}
wg.Done()
return counter
}
func bar() int {
i := 0
for i = 0; i < 10; i++ {
mutex2.Lock()
counter += 2
fmt.Println("count of bar: \t", counter)
mutex2.Unlock()
}
wg.Done()
return counter
}
我发现,如果我用time.sleep延迟其中一个函数,它就能运行,但这样不就不是并发代码了吗?
func bar() int {
time.Sleep(time.Nanosecond * 1)
i := 0
for i = 0; i < 10; i++ {
mutex2.Lock()
counter += 2
fmt.Println("count of bar: \t", counter)
mutex2.Unlock()
}
wg.Done()
return counter
}
迫不及待想听听大家对上述问题的看法,非常感谢任何帮助。我才刚开始编码三周,发现要完全理解它有点困难。
非常感谢。
更多关于Golang中为什么这段代码无法运行?求解答的实战教程也可以访问 https://www.itying.com/category-94-b0.html
你好Mikoj,
感谢你的回复,我已经尝试使用一个互斥锁,但它仍然存在竞态条件。我想请问一下,为什么你使用了mutex.Lock()并在互斥锁内部没有任何内容的情况下使用了defer?
正如我之前在帖子中提到的,你可以延迟其中一个函数,这样它就能工作,但这真的是并行代码吗?
非常感谢你帮助我理解。
Sea。
更多关于Golang中为什么这段代码无法运行?求解答的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Sean_Murphy:
你好 Mikoj,
感谢你的回复,我尝试使用一个互斥锁,但它仍然存在竞态条件。我想请问一下,为什么你使用了
mutex.Lock()和defer,但互斥锁内部没有任何代码?
嗯,里面并不是什么都没有,我有返回代码在里面。
首先你需要了解 defer 是如何工作的:
https://blog.golang.org/defer-panic-and-recover
https://medium.com/a-journey-with-go/go-how-does-defer-statement-work-1a9492689b6e
Sean_Murphy:
正如我之前在帖子中提到的,你可以延迟其中一个函数,这样确实能工作。但这真的是并行代码吗?
非常感谢你帮助我理解。
Sea.
使用一个互斥锁
package main
import (
"fmt"
"runtime"
"sync"
)
var counter int
var wg sync.WaitGroup
var mutex = sync.Mutex{}
func main() {
fmt.Println("CPU count Start: \t", runtime.NumCPU())
fmt.Println("GoRoutines count Start: \t", runtime.NumGoroutine())
wg.Add(2)
go boo()
go bar()
wg.Wait()
fmt.Println("GoRoutines count End: \t", runtime.NumGoroutine())
fmt.Println("the end : :)")
}
func boo() int {
defer wg.Done()
i := 0
for i = 0; i < 10; i++ {
mutex.Lock()
counter++
fmt.Println("count of boo: \t", counter)
mutex.Unlock()
}
mutex.Lock()
defer mutex.Unlock()
return counter
}
func bar() int {
defer wg.Done()
i := 0
for i = 0; i < 10; i++ {
mutex.Lock()
counter += 2
fmt.Println("count of bar: \t", counter)
mutex.Unlock()
}
mutex.Lock()
defer mutex.Unlock()
return counter
}
你的代码存在两个关键问题:
-
使用了两个不同的互斥锁:
mutex和mutex2分别保护boo()和bar()函数,但它们都在操作同一个共享变量counter。这实际上没有提供任何保护,因为两个协程仍然可以同时访问counter。 -
竞态检测器未启用:你需要使用
-race标志来编译和运行程序,才能检测到竞态条件。
下面是修复后的代码:
package main
import (
"fmt"
"runtime"
"sync"
)
var counter int
var wg sync.WaitGroup
var mutex = sync.Mutex{} // 使用同一个互斥锁
func main() {
fmt.Println("CPU count Start: \t", runtime.NumCPU())
fmt.Println("GoRoutines count Start: \t", runtime.NumGoroutine())
wg.Add(2)
go boo()
go bar()
wg.Wait()
fmt.Println("GoRoutines count End: \t", runtime.NumGoroutine())
fmt.Println("Final counter value: \t", counter)
fmt.Println("the end : :)")
}
func boo() {
defer wg.Done()
for i := 0; i < 10; i++ {
mutex.Lock()
counter++
fmt.Println("count of boo: \t", counter)
mutex.Unlock()
}
}
func bar() {
defer wg.Done()
for i := 0; i < 10; i++ {
mutex.Lock()
counter += 2
fmt.Println("count of bar: \t", counter)
mutex.Unlock()
}
}
运行这个程序时使用竞态检测:
go run -race main.go
输出示例:
CPU count Start: 8
GoRoutines count Start: 1
count of boo: 1
count of bar: 3
count of boo: 4
count of bar: 6
count of boo: 7
count of bar: 9
count of boo: 10
count of bar: 12
count of boo: 13
count of bar: 15
count of boo: 16
count of bar: 18
count of boo: 19
count of bar: 21
count of boo: 22
count of bar: 24
count of bar: 26
count of boo: 27
GoRoutines count End: 1
Final counter value: 30
the end : :)
关键修改:
- 两个函数使用同一个
mutex来保护counter变量 - 移除了函数返回值(因为
wg.Done()在循环后执行) - 使用
defer wg.Done()确保函数退出时一定会调用 - 简化了循环变量的声明
这样修改后,两个协程会交替执行,每次对 counter 的访问都受到互斥锁的保护,最终 counter 的值会是 30(10次+1和10次+2)。

