比尔萄堡

上帝说: “要有光” 于是,就有了光。


  • 首页

  • 归档

  • 关于我

  • 公益404

  • 搜索

data race

时间: 2022-10-20 分类: Golang   字数: 1127 字 阅读: 3分钟 阅读次数:

走进科学

发现问题

从一段问题代码开始

var (
    lock     sync.Mutex
    instance *UserInfo
)

func getInstance() (*UserInfo, error) {
    if instance == nil {
        lock.Lock()
        defer lock.Unlock()
        if instance == nil {
            instance = &UserInfo{
                Name: "fan",
            }
        }
    }
    return instance, nil
}

执行 go run -race go_race2.go 出现 WARNING: DATA RACE

什么是data race

Data races occur when multiple tasks or threads access a shared resource without sufficient protections, leading to undefined or unpredictable behavior.

在go里边,data race 意味着,多个goroutine访问(读/写)某个变量时,违反读写一致性。

问题定位

instance = &UserInfo{
   Name: "fan",
}

在这个位置 ,这一条命令,可以理解为三个指令 1. 创建UserInfo对象 2. 给该对象的Name属性赋值“fan” 3. 将该对象的地址传递给 instance 对于CPU的乱序指令操作,可能出现执行顺序为 1-3-2。 当 goroutine A 执行该函数时,若CPU执行指令刚好是1-3-2,在执行到3时,goroutine B 也执行该函数,此时判断 instance == nil时为false,就会返回一个 Name为空的UserInfo。

Memory Model

In computing, a memory model describes the interactions of threads through memory and their shared use of the data.

不同的语言,有不同的Memory Model,即 有不同的方式来处理多线程下的共享内存。主要解决了 CPU缓存一致性 和 CPU乱序执行 的问题。

CPU Cache

CPU缓存分为

  • L1(单核独享)
    • L1-I
    • L1-D
  • L2(单核独享)
  • L3(多核共享)

    一致性协议

    MESI协议中cache line有4个状态:

  • 失效(Invalid)

  • 共享(Shared)

  • 独占(Exclusive)

  • 已修改(Modified)

假如有4个core,core0访问 x,它的cache line状态为 Exclusive,另外3个core也访问x,状态为 Shared。core0修改x之后,它的cache line状态为 Modified,其他的core状态改为 Invalid。

流水线与乱序执行

CPU性能=IPC(CPU每一时钟周期内所执行的指令多少)×频率(MHz时钟速度) 为了提高性能,可以增加多个核,增加单核的并行度。

流水线是为了加速了指令通过速度

仍会存在几个问题: - 指令长度不同,产生气泡 - 指令存在依赖关系 - 指令存在条件分支

所有又出现了 两个优化点: - 乱序执行 - 分支预测

对于有依赖情况的指令,仍需保证顺序执行,所以提供了 内存屏障(Memory barrier),避免 乱序执行的优化,影响到最终结果。 比如x86的内存屏障指令:

lfence (asm), void _mm_lfence (void) 读操作屏障 
sfence (asm), void _mm_sfence (void)[1] 写操作屏障 
mfence (asm), void _mm_mfence (void)[2] 读写操作屏障

Go sync

Happens Before 是 Memory Model 中一个通用的概念。 要保证 W1 被 R2 读到,需满足: - W1发生在R2之前 - W1和R2之间的时间点,不能有其他写操作

golang实现 Happens Before 的地方: - init函数 - channel - sync - Mutex/RWMutex - sync - atomic - sync-once

CAS

compare and swap,用于在多线程编程中实现不被打断的数据交换操作,从而避免多线程同时改写某一数据时由于执行顺序不确定性以及中断的不可预知性产生的数据不一致问题。

ABA问题:地址X的值从 A 改为 B,又改回 A,这种情况CAS会失效。

引用链接:
一个 Code Review 引发的思考
Data Race Detector

#race# #memory#

标题:data race

作者:训导主任林作栋

声明: 本博客文章除特别声明外,均采用 CC BY-NC-SA 3.0许可协议,转载请注明出处!

创作实属不易,如有帮助,那就打赏博主些许茶钱吧 ^_^
WeChat Pay

微信打赏

Alipay

支付宝打赏

serial vs sequence
gorm更新时的空值与nil
训导主任林作栋

训导主任林作栋

戏谑的码农,北野武的拥趸,李安的迷弟,伍佰的Fans

55 日志
23 分类
62 标签
豆瓣
友情链接
  • Obsidian
  • V2EX
  • 关注黑客与极客
  • 廖雪峰
标签云
  • Linux 6
  • K8s 5
  • 数据库 5
  • Golang 4
  • Etcd 3
  • Mysql 3
  • Ai 2
  • Gitlab 2
  • Postgres 2
  • 前端 2
© 2010 - 2024 比尔萄堡
Powered by - Hugo v0.58.2 / Theme by - NexT
/
Storage by Vercel / 刘 ICP 备 18047355 号
0%