【Go】内存模型中的内存可见性

前言

使用go必然会使用到协程以及其他的并发操作,初期学习的时候,经常在启动协程时操作变量出现问题,要么就是变量没更新,要么就是各种崩溃,或者vscode报告警之类的,于是浅看了一下Go的内存模型,也了解到Happens Before的概念,这里记录一下;

原文链接:go内存模型,可能要挂梯子

正文

原文开篇的建议部分很有意思:

If you must read the rest of this document to understand the behavior of your program, you are being too clever.
Don’t be clever.

其实是建议大家好好看看内存模型的详述内容,不要自作聪明;

内存模型描述了程序执行的需求,这里的程序执行指的是由goroutine执行创建的(goroutine executions);一个内存操作由4种信息定义:

  • 操作的类型,表明它是一个普通的数据读取,普通的数据写入,还是一个同步操作,如原子数据访问,互斥操作,或通道操作;
  • 在代码中的位置;
  • 被访问的内存位置或变量;
  • 操作读取或写入的值;

goroutine executions为由单个goroutine执行的一组内存操作,我的理解就是一个协程中发生的内存操作;

关键概念

为了便于更好的理解后面的内容,我们需要理解几种关系术语:

happens-before(先于发生):

当线程1中的操作A先执行,而线程2中的操作B后执行时,A就happens-beforeB。happens-before是用来表示两个线程中两个操作被执行的先后顺序的一种描述。

happens-bofore有三个特点:

可传递性。如果Ahappens-beforeB,Bhappens-beforeC,则有Ahappens-beforeC;
当store操作A与load操作B发生同步时,则Ahappens-beforeB;
happens-before一般用于描述分别位于两个线程中的操作之间的顺序。

sequenced-before

如果在单个线程内操作A发生在操作B之前,则表示为Asequenced-beforeB。这个关系是描述单个线程内两个操作之前的先后执行顺序的,与happens-before是相对的。

此外,sequenced-before也具有可传递性,并且sequenced-beforehappences-before之间也具有可传递性:如果线程1中操作Asequenced-before操作B,而操作Bhappences-before线程2中的操作C,操作Csequenced-before线程2中的操作D,则有操作Ahappences-before操作D。

参考文章:
聊聊内存模型和内存序
C++11内存模型完全解读-从硬件层面和内存模型规则层面双重解读

数据竞争

首先,对数据竞争进行定义,数据竞争对内存位置的写入与对同一位置的另一个读或写同时发生,除非所涉及的所有访问都是由sync/atomic包提供的操作,理解来说就是操作是否是数据安全的。

以下模拟了一个发生数据竞争的场景:

func main() {
	var a int
	go func() {
		a = 2
		fmt.Println("goroutine: ", a)
	}()

	go func() {
		a = 3
		fmt.Println("goroutine: ", a)
	}()
	a = 10
	time.Sleep(1 * time.Second)
}

启动2个协程,同时修改a的值,测试运行,加上-race参数,将会打印数据竞争的发生情况:
在这里插入图片描述

Happens Before

Happens Before也叫先行发生,这也是Go中的读写操作要求,先行发生是在 Go 程序的内存操作中局部的执行顺序,既然有Happens Before那么就有Happens After,假设有两个事件e1和e2,有以下概念:

  • e1发生于e2之前:e1 Happens Before 于 e2
  • e1发生于e2之后:e1 Happens After 于 e2
  • e1既不发生于e2之前,也不发生于之后:e1,e2同时发生

几个特征

特征一

对于每个协程内的内存操作,从内存中读取值或者向内存中写入值,都应该是符合正确的顺序执行;也就是运行流程符合sequenced before关系,因为在单个协程内,不存在并发;

一个Go程序运行会被抽象为一组goroutine执行,并伴随一个Map W;W中会指定每个读类型操作的来源写操作

特征二

如果一个同步的读类内存操作r观察到一个同步的写类内存操作w,那么w synchronized before r

特征三

对于一个普通操作(非同步的)数据R,内存地址为X,满足以下条件时对R的写操作w对读操作r可见:

  • w happens before r;
  • 不存在其他的写操作w1 happens after w,且happens before r,即这样的结构:w happens before w1 happens before r;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Meepoljd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值