0%

Go内存模型

在Go语言中,变量、结构体、成员、函数、接口等第一个字母的大小写决定了其访问权限,大写字母开头表示公共的,可供外部调用;小写字母开头表示私有的,外部不能调用。

一、基础

      Go内存模型明确指出,一个goroutine如何才能观察到其他goroutine对同一变量的写操作。当多个goroutine并发同时读写同一个数据时必须把并发的存取操作序列化,在Go中保证读写的序列化可以通过channel通信或者其他同步原语实现(例如sync包中的互斥锁、读写锁和sync/atomic中的原子操作)。

解决多goroutine下共享数据可见性问题的方法是在访问共享数据时候施加一定的同步措施

二、延伸

  1. happens-before

    • 条件一:如果对于一个变量v的读操作r和写操作w满足下述两个条件,r才允许观察到w:
      • r没有发生在w之前
      • 没有其他写操作发生在w之后和r之前
    • 条件二:为了保证变量v的一个读操作r能够观察到一个特定的写操作w,需要确保w是唯一允许被r观察的写操作。那么如果 r、w都满足以下条件,r就能确保观察到w:
      • w发生在r之前
      • 其他写操作发生在w之前后者r之后
  2. Go中happens-before的保证

    • 单线程

    • Init函数

      • 如果包P1中导入了包P2,则P2中的init函数Happens Before 所有P1中的操作
      • main函数Happens After 所有的init函数
    • Goroutine

      • Goroutine的创建Happens Before所有此Goroutine中的操作
      • Goroutine的销毁Happens After所有此Goroutine中的操作
    • Channel

      • 对一个元素的send操作Happens Before对应的receive完成操作——[先发后接]
      • 对channel的close操作Happens Before receive端的收到关闭通知操作——[先关后接 接到零值]
      • 对于无缓冲信道,对一个元素的receive操作Happens Before对应的send完成操作——[先接后发]
      • 对于有缓冲信道,假设Channel的buffer大小为C,那么对第k个元素的receive操作Happens Before第k+C个send完成操作。

        可以看出上一条Unbuffered Channel规则就是这条规则C=0时的特例 [先接后发]

    • Lock

      • Go里面有Mutex和RWMutex两种锁,RWMutex除了支持互斥的Lock/Unlock,还支持共享的RLock/RUnlock。
      • 对于一个Mutex/RWMutex,设n < m,则第n个Unlock操作Happens Before第m个Lock操作。
      • 对于一个RWMutex,存在数值n,RLock操作Happens After 第n个UnLock,其对应的RUnLock Happens Before 第n+1个Lock操作。

        简单理解就是这一次的Lock总是Happens After上一次的Unlock,读写锁的RLock HappensAfter上一次的UnLock,其对应的RUnlock Happens Before 下一次的Lock。

    • Once:sync包的Once为多个goroutine提供了安全的初始化机制。能在多个线程中执行once.Do(f),但只有一个f()会执行,其他调用会一直阻塞直到f()返回。

三、参考

  1. 参考一
  2. 参考二
  3. 参考三
  4. 参考四
  5. 参考五
  6. 参考六