美文网首页
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