GO-并发安全
作者:互联网
资源竞争
-
多协程并发修改同一块内存,产生资源竞争
-
go run或go build时添加-race参数检查资源竞争情况
-
n++不是原子操作,并发执行时会存在脏写。n++分为3步:取出n,加1,结果赋给n
脏写现象
package main import ( "fmt" "sync" "sync/atomic" ) var n int32 func inc() { n++ } func main() { const P = 100 wg := sync.WaitGroup{} wg.Add(P) for i := 0; i < P; i++ { go func() { defer wg.Done() inc() }() } wg.Wait() fmt.Printf("n=%d\n", n) } // go run .\atomic_channel.go 概率性出现 n=99 // go run .\atomic_channel.go n=100
原子操作
使用atomic封装原子操作,解除资源竞争,避免脏写
- atomic.LoadInt32
- atomic.AddInt32
package main
import (
"fmt"
"sync"
"sync/atomic"
)
var n int32
func inc() {
n++
}
func inc2() {
atomic.AddInt32(&n, 1)
}
func main() {
const P = 100
wg := sync.WaitGroup{}
wg.Add(P)
for i := 0; i < P; i++ {
go func() {
defer wg.Done()
inc2()
}()
}
wg.Wait()
fmt.Printf("n=%d\n", n)
// fmt.Printf("n=%d\n", atomic.LoadInt32(&n))
}
// go run .\atomic_channel.go
n=100
读写锁
- 读写锁包含sync.Mutex,一般用读写锁
- var lock2 sync.RWMutex 声明读写锁,无需初始化
- lock.Lock() lock.Unlock 加写锁和释放写锁
- lock.RLock() lock.RUnlock() 加读锁和释放读锁
- 任意时刻只可以加一把写锁,且不能再加读锁
- 没加写锁时,可以同时加多把读锁
示例
var lock sync.RWMutex
func main() {
lock.Lock // 写锁
n++ // 临界区-锁和释放锁之间的代码
lock.Unlock // 释放写n锁
lock.RLock //读锁
fmt.Printf("n=%d\n", n)
lock.RUnlock //释放读锁
}
容器的并发安全性
- 数组、slice、struct允许并发修改(可能会脏写),并发修改map会发生panic
- 如果需要并发修改map请使用sync.Map
并发修改数组
package main
import (
"fmt"
"sync"
)
var arr = [10]int{}
func main() {
wg := sync.WaitGroup{}
wg.Add(2)
go func() {
defer wg.Done()
for i := 1; i < 10; i += 2 {
arr[i] = 3
}
}()
go func() {
defer wg.Done()
for i := 0; i < 10; i += 2 {
arr[i] = 4
}
}()
wg.Wait()
fmt.Println(arr)
}
// go run .\collection_safety.go
[4 3 4 3 4 3 4 3 4 3]
并发修改slice
package main
import (
"fmt"
"sync"
)
var arr = make([]int, 10)
func main() {
wg := sync.WaitGroup{}
wg.Add(2)
go func() {
defer wg.Done()
for i := 1; i < 10; i += 2 {
arr[i] = 3
}
}()
go func() {
defer wg.Done()
for i := 0; i < 10; i += 2 {
arr[i] = 4
}
}()
wg.Wait()
fmt.Println(arr)
}
// go run .\collection_safety.go
[4 3 4 3 4 3 4 3 4 3]
并发修改struct
package main
import (
"fmt"
"sync"
)
type Student struct {
Name string
Age int
}
var student = Student{Name: "tom", Age: 18}
func main() {
wg := sync.WaitGroup{}
wg.Add(2)
go func() {
defer wg.Done()
student.Name = "test1"
}()
go func() {
defer wg.Done()
student.Name = "test2"
student.Age = 22
}()
wg.Wait()
fmt.Println(student)
}
// go run .\collection_safety.go
{test1 22}
// go run .\collection_safety.go
{test2 22}
并发修改map
package main
import (
"fmt"
"sync"
)
var dict = make(map[int]string, 10)
func main() {
wg := sync.WaitGroup{}
wg.Add(2)
go func() {
defer wg.Done()
dict[1] = "Tom"
}()
go func() {
defer wg.Done()
dict[1] = "jieke"
}()
wg.Wait()
for i, v := range dict {
fmt.Printf("%d: %s\n", i, v)
}
}
// 概率性报错
// go run .\collection_safety.go
fatal error: concurrent map writes
goroutine 6949 [running]:
runtime.throw(0x4d2ca3, 0x15)
E:/Go/src/runtime/panic.go:1116 +0x79 fp=0xc000041f50 sp=0xc000041f20 pc=0x432f99
runtime.mapassign_fast64(0x4b5d40, 0xc00006e330, 0x1, 0xc000050528)
E:/Go/src/runtime/map_fast64.go:176 +0x311 fp=0xc000041f90 sp=0xc000041f50 pc=0x410461
main.main.func1(0xc0000120a0)
E:/GO_projectv/collection_safety.go:25 +0x75 fp=0xc000041fd8 sp=0xc000041f90 pc=0x4a0845
runtime.goexit()
E:/Go/src/runtime/asm_amd64.s:1373 +0x1 fp=0xc000041fe0 sp=0xc000041fd8 pc=0x45daa1
created by main.main
E:/GO_project/collection_safety.go:23 +0x8d
goroutine 1 [semacquire]:
sync.runtime_Semacquire(0xc0000120a8)
E:/Go/src/runtime/sema.go:56 +0x49
sync.(*WaitGroup).Wait(0xc0000120a0)
E:/Go/src/sync/waitgroup.go:130 +0x6b
main.main()
E:/GO_project/collection_safety.go:31 +0xbd
exit status 2
使用sync.Map
- var dict sync.Map 定义sync.Map
- dict.Store(2,"jieke") 赋值
- dict.Range 遍历
package main
import (
"fmt"
"sync"
)
var dict sync.Map
func main() {
wg := sync.WaitGroup{}
for i := 0; i < 10000000; i++ {
wg.Add(2)
go func() {
defer wg.Done()
dict.Store(1, "tom")
}()
go func() {
defer wg.Done()
dict.Store(2, "jieke")
}()
wg.Wait()
}
dict.Range(func(key, value interface{}) bool {
fmt.Printf("%d: %s\n", key.(int), value.(string))
return true
})
}
标签:wg,func,安全,fmt,sync,并发,go,GO,main 来源: https://www.cnblogs.com/Otiger/p/16221792.html