1.背景
記錄一下,方便后續寫代碼直接使用。
需要注意幾點:
- chan 默認支持多協程工作,不需要加鎖。
- 其他變量操作需要使用鎖保護(map多協程並發寫會panic, 並且無法捕獲)。
- 啟動goroutine時, 通常需要傳遞參數。不讀取局部變量。
- 需要使用waitgroup等待所有goroutine的退出(即使部分goroutine出現panic也需要wg.Done())
- 每個goroutine都必須捕獲panic, 否則panic會導致進程會掛掉。
2. 統一panic判斷函數:COMMON_PANIC_CAPTURE
在工作中遇到過,由於panic 日志打印不統一在panic監控出現漏報情況。
通過封裝panic判斷函數,統一日志打印,方便監控添加,避免漏報情況。
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))
}
