0
点赞
收藏
分享

微信扫一扫

使用互斥锁解决goroutine之间的资源竞争问题


面临问题 资源竞争

当多个goroutine同时对一个资源进行读写时,会出现资源竞争问题。如下例所示

package main


import (
"fmt"
"time"
)

/*
1. 计算1-200内的素数,并放到map中
2. 用goroutine实现
*/

var mymap = make(map[int]int)
var count int = 0


func main() {
fmt.Println("hello")
for i:=1;i<=200;i++ {
go getPrime(i)
}

time.Sleep(20 * time.Second)

fmt.Println(mymap)


}

// 给定一个正整数,打印包含的素数
func getPrime(num int) {
if isPrieme(num){
mymap[count] = num
count++

}
}
// 判断一个数是否是素数
func isPrieme(num int) bool {
for i:=2;i<num;i++{
if num%i == 0 {
return false
}
}
return true
}


  • 运行程序报错信息如下
(base) ➜  01goroutine ./main                          

==================
WARNING: DATA RACE
Read at 0x0000011fd788 by goroutine 8:
main.getPrime()
/Users/80280051/Documents/study/go/study/202103goroutine/01goroutine/main.go:14 +0x7c
main.main·dwrap·1()
/Users/80280051/Documents/study/go/study/202103goroutine/01goroutine/main.go:19 +0x39

Previous write at 0x0000011fd788 by goroutine 7:
main.getPrime()
/Users/80280051/Documents/study/go/study/202103goroutine/01goroutine/main.go:30 +0xd5
main.main·dwrap·1()
/Users/80280051/Documents/study/go/study/202103goroutine/01goroutine/main.go:19 +0x39

Goroutine 8 (running) created at:
main.main()
/Users/80280051/Documents/study/go/study/202103goroutine/01goroutine/main.go:19 +0xbd

Goroutine 7 (finished) created at:
main.main()
/Users/80280051/Documents/study/go/study/202103goroutine/01goroutine/main.go:19 +0xbd
==================
==================
WARNING: DATA RACE
Write at 0x00c000122180 by goroutine 9:
runtime.mapassign_fast64()
/usr/local/go/src/runtime/map_fast64.go:92 +0x0
main.getPrime()
/Users/80280051/Documents/study/go/study/202103goroutine/01goroutine/main.go:29 +0x94
main.main·dwrap·1()
/Users/80280051/Documents/study/go/study/202103goroutine/01goroutine/main.go:19 +0x39

Previous write at 0x00c000122180 by goroutine 7:
runtime.mapassign_fast64()
/usr/local/go/src/runtime/map_fast64.go:92 +0x0
main.getPrime()
/Users/80280051/Documents/study/go/study/202103goroutine/01goroutine/main.go:29 +0x94
main.main·dwrap·1()
/Users/80280051/Documents/study/go/study/202103goroutine/01goroutine/main.go:19 +0x39

Goroutine 9 (running) created at:
main.main()
/Users/80280051/Documents/study/go/study/202103goroutine/01goroutine/main.go:19 +0xbd

Goroutine 7 (finished) created at:
main.main()
/Users/80280051/Documents/study/go/study/202103goroutine/01goroutine/main.go:19 +0xbd
==================
==================
WARNING: DATA RACE
Write at 0x00c000180050 by goroutine 9:
main.getPrime()
/Users/80280051/Documents/study/go/study/202103goroutine/01goroutine/main.go:29 +0xa4
main.main·dwrap·1()
/Users/80280051/Documents/study/go/study/202103goroutine/01goroutine/main.go:19 +0x39

Previous write at 0x00c000180050 by goroutine 8:
main.getPrime()
/Users/80280051/Documents/study/go/study/202103goroutine/01goroutine/main.go:29 +0xa4
main.main·dwrap·1()
/Users/80280051/Documents/study/go/study/202103goroutine/01goroutine/main.go:19 +0x39

Goroutine 9 (running) created at:
main.main()
/Users/80280051/Documents/study/go/study/202103goroutine/01goroutine/main.go:19 +0xbd

Goroutine 8 (finished) created at:
main.main()
/Users/80280051/Documents/study/go/study/202103goroutine/01goroutine/main.go:19 +0xbd
==================
==================
WARNING: DATA RACE
Read at 0x00c000076218 by main goroutine:
reflect.typedmemmove()
/usr/local/go/src/runtime/mbarrier.go:178 +0x0
reflect.copyVal()
/usr/local/go/src/reflect/value.go:1651 +0x6e
reflect.(*MapIter).Value()
/usr/local/go/src/reflect/value.go:1605 +0x13b
internal/fmtsort.Sort()
/usr/local/go/src/internal/fmtsort/sort.go:65 +0x2b1
fmt.(*pp).printValue()
/usr/local/go/src/fmt/print.go:769 +0x739
fmt.(*pp).printArg()
/usr/local/go/src/fmt/print.go:712 +0xdf4
fmt.(*pp).doPrintln()
/usr/local/go/src/fmt/print.go:1169 +0x225
fmt.Fprintln()
/usr/local/go/src/fmt/print.go:264 +0x5d
fmt.Println()
/usr/local/go/src/fmt/print.go:274 +0x124
main.main()
/Users/80280051/Documents/study/go/study/202103goroutine/01goroutine/main.go:23 +0xcf

Previous write at 0x00c000076218 by goroutine 37:
main.getPrime()
/Users/80280051/Documents/study/go/study/202103goroutine/01goroutine/main.go:29 +0xa4
main.main·dwrap·1()
/Users/80280051/Documents/study/go/study/202103goroutine/01goroutine/main.go:19 +0x39

Goroutine 37 (finished) created at:
main.main()
/Users/80280051/Documents/study/go/study/202103goroutine/01goroutine/main.go:19 +0xbd
==================
map[0:1 1:3 3:5 4:7 5:11 6:13 7:17 8:19 9:23 10:29 11:31 12:37 13:41 14:43 15:47 16:53 17:59 18:61 19:67 20:71 21:73 22:79 23:83 24:89 25:97 26:101 27:103 28:107 29:109 30:113 31:127 32:131 33:137 34:139 35:149 36:151 37:157 38:163 39:167 40:173 41:179 42:181 43:191 44:193 45:197 46:199]
Found 4 data race(s)


原因: 由于多个goroutine同时对map进行写入,出现了资源竞争race


解决方法

通常有2种方法来解决

  • 使用互斥锁,在操作资源前,先对资源加锁,操作完后再解锁。
  • 使用管道


使用互斥锁解决资源竞争的问题

package main


import (
"fmt"
"sync"
"time"
)

/*
1. 计算1-200内的素数,并放到map中
2. 用goroutine实现
*/

var mymap = make(map[int]int)
var count int = 0
var lock sync.Mutex

func main() {
fmt.Println("hello")
for i:=1;i<=200;i++ {
go getPrime(i)
}

time.Sleep(20 * time.Second)

lock.Lock()
fmt.Println(mymap)
lock.Unlock()

}

// 给定一个正整数,打印包含的素数
func getPrime(num int) {
if isPrieme(num){
lock.Lock()
mymap[count] = num
count++
lock.Unlock()
}
}
// 判断一个数是否是素数
func isPrieme(num int) bool {
for i:=2;i<num;i++{
if num%i == 0 {
return false
}
}
return true
}
  • 运行结果
(base) ➜  01goroutine time  go build  --race   main.go
go build --race main.go 0.27s user 0.98s system 90% cpu 1.379 total
(base) ➜ 01goroutine ./main
hello
map[0:1 1:2 2:3 3:5 4:7 5:11 6:13 7:17 8:19 9:23 10:29 11:31 12:37 13:41 14:43 15:47 16:53 17:59 18:61 19:67 20:71 21:73 22:79 23:83 24:89 25:97 26:101 27:103 28:107 29:109 30:113 31:127 32:131 33:137 34:139 35:149 36:151 37:157 38:163 39:167 40:173 41:179 42:181 43:191 44:193 45:197 46:199]
(base) ➜ 01goroutine


背景知识

”锁“,分为两种,一种是互斥锁,一种是读写锁

  • 互斥锁: 即互斥不可以同时操作。类比厕所坑位,只能一个人使用,进去后需要加锁,用完后才能打开锁。否则就尴尬了。
  • 读写锁: 对某个资源可以单写多读。类比一本书,可能多个人同时读,但只能一个人写。




举报

相关推荐

0 条评论