Go 工具套件在 Go 版本 1.1 引入了一个竞态检测工具(race detector)。这个竞态检测工具是在编译流程中内置到你程序的代码。一旦你的程序开始运行,它能够发现和报告任何他所检测到的竞态情况
package main
import (
"fmt"
"sync"
"time"
)
var Wait sync.WaitGroup
var Counter int = 0
func main() {
for routine := 1; routine <= 2; routine++ {
Wait.Add(1)
go Work(routine)
}
Wait.Wait()
fmt.Printf("Final Counter: %d\n", Counter)
}
func Work(id int) {
for i := 0; i < 10; i++ {
Counter++
time.Sleep(1 * time.Nanosecond)
}
Wait.Done()
}
使用go build -race -o main.out main.go
(也可以直接使用go run -race
),然后运行:
[root@admin go-race]# go build -race -o main.out main.go
[root@admin go-race]# ls
[root@admin go-race]# ./main.out
==================
WARNING: DATA RACE
Read at 0x000000609908 by goroutine 8:
main.Work()
/root/code/go_work/project/gotour/go-race/main.go:24 +0x47
Previous write at 0x000000609908 by goroutine 7:
main.Work()
/root/code/go_work/project/gotour/go-race/main.go:24 +0x64
Goroutine 8 (running) created at:
main.main()
/root/code/go_work/project/gotour/go-race/main.go:15 +0x75
Goroutine 7 (running) created at:
main.main()
/root/code/go_work/project/gotour/go-race/main.go:15 +0x75
==================
Final Counter: 20
Found 1 data race(s)
加锁,修改之:
package main
import (
"fmt"
"sync"
"time"
)
var Wait sync.WaitGroup
var Counter int = 0
var CounterLock sync.Mutex
func main() {
for routine := 1; routine <= 2; routine++ {
Wait.Add(1)
go Work(routine)
}
Wait.Wait()
fmt.Printf("Final Counter: %d\n", Counter)
}
func Work(id int) {
for i := 0; i < 10; i++ {
CounterLock.Lock()
Counter++
CounterLock.Unlock()
time.Sleep(1 * time.Nanosecond)
}
Wait.Done()
}
再次运行,检测通过。
go 语言常见并发问题
-
并发访问 map
map 并发访问是非安全的。 -
循环变量被捕获
老生常谈的问题了。for _ , job := range jobs { go func () { ProcessJob ( job ) }() }
-
err 变量被捕获
x , err := Foo () if err != nil { ... } go func () { var y int y , err = Bar () if err != nil { ... } }() var z string z , err = Baz () if err != nil { ... }
-
命名返回值被捕获
func NamedReturnCallee () ( result int) { result = 10 if ... { return // this has the effect of " return 10" } go func () { ... = result // read result }() return 20 // this is equivalent to result =20 } func Caller () { retVal := NamedReturnCallee () }
defer 同理
func Redeem ( request Entity ) ( resp Response , err error ) { defer func () { resp , err = c.Foo (request , err) }() err = CheckRequest ( request ) ... // err check but no return go func () { ProcessRequest (request, err != nil) }() return // the defer function runs after here }
- 传值和传引用的问题
比如go的并发库中的mutex 等不允许被copy,所以传递时需要使用指针的方式
网友评论