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判断函数,统一日志打印,方便监控添加,避免漏报情况。

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))
}

posted on 2019-06-02 08:54  旭东的博客  阅读(...)  评论(...编辑  收藏

导航