go併發編程 WaitGroup, Mutex

1.背景

記錄一下,方便後續寫代碼直接使用。
須要注意幾點:編程

  • chan 默認支持多協程工做,不須要加鎖。
  • 其餘變量操做須要使用鎖保護(map多協程併發寫會panic, 而且沒法捕獲)。
  • 啓動goroutine時, 一般須要傳遞參數。不讀取局部變量。
  • 須要使用waitgroup等待全部goroutine的退出(即便部分goroutine出現panic也須要wg.Done())
  • 每一個goroutine都必須捕獲panic, 不然panic會致使進程會掛掉。

2. 統一panic判斷函數:COMMON_PANIC_CAPTURE

在工做中遇到過,因爲panic 日誌打印不統一在panic監控出現漏報狀況。
經過封裝panic判斷函數,統一日誌打印,方便監控添加,避免漏報狀況。json

package main

import (
    "fmt"
    "runtime/debug"
    "sync"
    "time"
)

func COMMON_PANIC_CAPTURE(panicErr interface{}) (bool){ //封裝一個panic判斷/日誌打印函數
    if panicErr != nil {
        fmt.Printf("PANIC err:%v, stack:%s\n", panicErr, debug.Stack())
        return true
    }
    return false
}

func main() {
    var wg sync.WaitGroup
    for i := 0; i < 3; i=i+1 {
        go func(x int){  //啓動go時, 須要注意參數傳遞
            wg.Add(1)
            defer func() {
                wg.Done()
                COMMON_PANIC_CAPTURE(recover())
            }()
            if x == 2 {
                panic(fmt.Sprintf("val:%d", x))
            }
        }(i)
    }
    wg.Wait()   //等待go結束
    time.Sleep(2 * time.Second) //等待go panic日誌打印
    fmt.Printf("end ok!\n")
}

3. 併發編程例子

併發寫map panic, 程序沒法捕獲,多是go設計的一個問題。併發

package main

import (
    "fmt"
    "sync"
    "encoding/json"
)

func main() {
    res := make(map[string]string)
    var wg sync.WaitGroup   //group, 內部使用atomic實現計數
    var mylock sync.Mutex

    for i := 0; i < 4000; i++ {
        wg.Add(1)
        go func(par int){
            defer func() {
                wg.Done()
            }()

            //time.Sleep(3 * time.Second)
            tmp := fmt.Sprintf("%d", par %7)

            mylock.Lock()  //加鎖
            defer mylock.Unlock()  //defer確保解鎖
            res[tmp] = tmp
        }(i)
    }
    wg.Wait()

    resByte, _ := json.Marshal(res)
    fmt.Printf("%s\n", string(resByte))
}
相關文章
相關標籤/搜索