0
点赞
收藏
分享

微信扫一扫

data race介绍

江南北 2022-05-21 阅读 56

1.data race介绍

Go(从v1.1开始)具有内置的数据竞争检测器,data race是两个或多个goroutine访问同一个资源(如变量或数据结构),并尝试对该资源进行读写而不考虑其他goroutine。

1.1运行时检查竟态的命令:​go run -race main.go​

data race介绍_数据

1.2构建时检查竟态的命令:​go build -race main.go​

data race介绍_数据_02

1.3测试时检查竟态的命令:​go test -race main.go​

data race介绍_数据_03

race信息如下,能详细看出是代码的哪一行

data race介绍_数据_04

2.原子赋值问题

2.1如下程序,存在race

data race介绍_i++_05

2.2i++是原子赋值吗?否

但是我们通过查看底层汇编:

data race介绍_数据_06

实际上是有三行汇编代码在执行增加Counter。这三行汇编很可能存在上下文切换,从技术上讲,是并发不安全的。

data race介绍_数据_07

2.3struct赋值是原子赋值吗?否

data race介绍_赋值_08

上面程序可能会出现:Ben says,hello my name is Jerry,为什么呢???

interface内部实际是两个machine word值,如下结构

data race介绍_i++_09

当赋值语句var maker IceCreamMaker = ben执行时,编译器的操作是

data race介绍_数据_10

在上面例子中,Ben和Jerry的结构是相同的,因此某种意义上是兼容的。如果两个结构体字段不同呢?

那么可能会panic,因为结构不同,赋值会出问题。

如果是一个普通的指针、map、slice可以安全的更新吗?

不可以,没有安全的赋值,需要借助锁或automic 等实现全局变量的赋值。

 3.automic

3.1automic样例

读多写少使用读写锁

读特别多,可以尝试使用automic.value

看下面样例

config_test.go

package config

import (
"sync"
"sync/atomic"
"testing"
)

type Config struct {
a []int
}

func (c *Config)T() {

}
//automic处理
func BenchmarkAtomatic(b *testing.B) {
var v atomic.Value
v.Store(&Config{})
go func() {
i :=0
for{
i++
cfg := &Config{a:[]int{i,i+1,i+2,i+3,i+4,i+5}}
v.Store(cfg)
}
}()
var wg sync.WaitGroup
for n:=0;n<4;n++{
wg.Add(1)
go func() {
for n:=0;n<b.N;n++{
cfg := v.Load().(*Config)
cfg.T()
//fmt.Printf("%v\n",cfg)
}
wg.Done()
}()
}
wg.Wait()

}

//读写锁
func BenchmarkMutex(b *testing.B) {
var l sync.RWMutex
var cfg *Config
go func() {
i :=0
for{
i++
l.Lock()
cfg = &Config{a:[]int{i,i+1,i+2,i+3,i+4,i+5}}
l.Unlock()
}
}()
var wg sync.WaitGroup
for n:=0;n<4;n++{
wg.Add(1)
go func() {
for n:=0;n<b.N;n++{
l.RLock()
cfg.T()
l.RUnlock()
//fmt.Printf("%v\n",cfg)
}
wg.Done()
}()
}
wg.Wait()
}

automic耗时更少

data race介绍_赋值_11

3.2automic copy on write

Opy-On-Write思路在微服务降级或local cache场景中经常使用。写时复制指的是:写操作的时候复制全量老数据到一个新的对象中,携带上本次新写的数据,之后利用原子替换(automic.Value)更新调用者的变量,来完成无锁访问共享数据

data race介绍_赋值_12




举报

相关推荐

0 条评论