6、goroutine 和 sync.WaitGroup 使用不当的情况会引发会引发的问题及解法方法

33 阅读1分钟

1、竟态问题

func TestG1(t *testing.T) {
    count := 1
    for i := 0; i < 1000; i++ {
       go func() {
          count++
       }()
    }
    fmt.Println(count) // 963 977 ....
    
    // 正确做法
    count2 := 0
    lock := &sync.Mutex{}
    wg := &sync.WaitGroup{}
    for i := 0; i < 1000; i++ {
        wg.Add(1)
        go func() {
           defer wg.Done()
           lock.Lock()
           defer lock.Unlock()
           count2++
        }()
    }
    wg.Wait()
    fmt.Println(count2)
    
    // 正确做法
    count3 := atomic.Int64{}
    wg := &sync.WaitGroup{}
    for i := 0; i < 1000; i++ {
        wg.Add(1)
        go func() {
           defer wg.Done()
           count3.Add(1)
        }()
    }
    wg.Wait()
    fmt.Println(count3.Load())
}

2、闭包问题

func TestG2(t *testing.T) {
    wg := sync.WaitGroup{}
    onlyOne := 0
    for i := 0; i < 10; i++ {
       wg.Add(1)
       onlyOne = i
       go func() {
          defer wg.Done()
          fmt.Println(onlyOne)
       }()
    }
    wg.Wait()
    // 9 9 9 9 9 9 7 9 9
    
    // 正确做法
    wg2 := sync.WaitGroup{}
    for i := 0; i < 3; i++ {
        wg2.Add(1)
        go func() {
           defer wg2.Done()
           fmt.Println(i)
        }()
    }
    wg2.Wait()

    fmt.Println("--------------")
    wg3 := sync.WaitGroup{}
    for i := 0; i < 3; i++ {
        wg3.Add(1)
        go func(j int) {
           defer wg3.Done()
           fmt.Println(j) // 副本
        }(i)
    }
    wg3.Wait()

    fmt.Println("--------------")
    onlyOne4 := 0
    fmt.Println(fmt.Printf("onlyOne4 %p\n", &onlyOne4))
    wg4 := sync.WaitGroup{}
    for i := 0; i < 3; i++ {
        wg4.Add(1)
        onlyOne4 = i
        go func(onlyOne4 int) {
           defer wg4.Done()
           fmt.Println(fmt.Printf("onlyOne4 %p\n", &onlyOne4)) // 可以发现地址并不相同
           fmt.Println(onlyOne4) // 副本
        }(onlyOne4)
    }
    wg4.Wait()
}

三、死锁

func TestG3(t *testing.T) {
    wg := sync.WaitGroup{}
    wg.Add(11)
    for i := 0; i < 10; i++ {
       go func() {
          defer wg.Done()
          fmt.Println(time.Now())
       }()
    }
    wg.Wait()
}

func TestG32(t *testing.T) {
    wg := sync.WaitGroup{}
    wg.Add(1)
    receiveWG(wg) // 发生了复制,并不是原来的wg
    wg.Wait()
}
// 发生了复制,并不是原来的wg
func receiveWG(wg sync.WaitGroup) {
    wg.Done()
}

// 正确做法
func TestG33(t *testing.T) {
    wg := sync.WaitGroup{}
    wg.Add(1)
    receiveWG33(&wg) // 发生了复制,并不是原来的wg
    wg.Wait()
}
func receiveWG33(wg *sync.WaitGroup) {
    wg.Done()
}

四、携程泄露 (注意关闭资源)

func TestG4(t *testing.T) {
    go func() {
       ch := make(chan int)
       <-ch
    }()
    fmt.Println(runtime.NumGoroutine()) // 3 主 goroutine 测试框架启动的 goroutine  TestG4 函数中显式创建的匿名 goroutine
}