关于GoLang的concurrent map writes错误

作为一个GoLang萌新(其实就是并发编程萌新,之前一直在做PHP),对并发下共享资源的竞争了解不多。所以一开始写出了如下代码:

type OriginPriceController struct {
	OriginPriceService *origin_price_service.OriginPriceService
}


func (self *OriginPriceController) action() {
    var wg sync.WaitGroup
	wg.Add(2)
	go func() {
		defer wg.Done()
		self.Data["product"] = self.OriginPriceService.GetOriginPriceProduct(self.C, productId)
	}()

	go func() {
		defer wg.Done()
		self.Data["location"] = self.OriginPriceService.GetOriginPriceLocation(self.C, locationId)
	}()
	wg.Wait()
}

轻松搞定,并行请求数据,相比之前的串行,性能基本上提升一倍。本以为并发编程也不过如此,就起几个goroutine,用WaitGroup同步多个协程间的状态,等待所有协程都执行完即可。

原本以为这样就搞定了,其实代码中隐含着一个致命问题,当多次请求测试时,很快问题就暴露出来了:

fatal error: concurrent map writes

goroutine 164 [running]:
runtime.throw(0x17e99d3, 0x15)
        /usr/local/go/src/runtime/panic.go:774 +0x72 fp=0xc000069ee8 sp=0xc000069eb8 pc=0x102f6c2
runtime.mapassign_faststr(0x16fe080, 0xc000574150, 0x17e0ed5, 0x8, 0x0)
        /usr/local/go/src/runtime/map_faststr.go:291 +0x3fe fp=0xc000069f50 sp=0xc000069ee8 pc=0x1014d8e
gin-frame/controllers/first_origin_price.(*FirstOriginPriceController).action.func2(0xc00029e550, 0xc00014e180, 0x571)
        /Users/why/Desktop/go/gin-frame/controllers/first_origin_price/first_origin_price_controller.go:62 +0xc4 fp=0xc000069fc8 sp=0xc000069f50 pc=0x164e0d4
runtime.goexit()
        /usr/local/go/src/runtime/asm_amd64.s:1357 +0x1 fp=0xc000069fd0 sp=0xc000069fc8 pc=0x105cd81
created by gin-frame/controllers/first_origin_price.(*FirstOriginPriceController).action
        /Users/why/Desktop/go/gin-frame/controllers/first_origin_price/first_origin_price_controller.go:60 +0x156

没错,就是文章题目里提到的:concurrent map writes

原来Map,对于共享变量,资源,并发写会产生竞争, 共享资源遭到破坏,解决办法是拆分成两个局部变量用于接收goroutine,在goroutine执行完之后再将结果存入公共Map,相应代码如下:

    var wg sync.WaitGroup
	product := make(map[string]interface{})
	location := make(map[string]interface{})
	wg.Add(2)
	go func() {
		defer wg.Done()
		product = self.OriginPriceService.GetOriginPriceProduct(self.C, productId)
	}()

	go func() {
		defer wg.Done()
		location = self.OriginPriceService.GetOriginPriceLocation(self.C, locationId)
	}()
	wg.Wait()

	self.Data["product"] = product
	self.Data["location"] = location

 

还可以使用sync.Map解决,除了使用互斥量以外,还运用了原子操作,可以保证并发写安全,但是有可能出现锁Rate。

你可能感兴趣的:(go,concurrent,map,map异常,互斥,并发)