美文网首页
go 竞态检测

go 竞态检测

作者: wayyyy | 来源:发表于2022-03-19 02:22 被阅读0次

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,所以传递时需要使用指针的方式

相关文章

网友评论

      本文标题:go 竞态检测

      本文链接:https://www.haomeiwen.com/subject/sucgdrtx.html