GO内存模型(同步机制)

文章目录

        • 概念
          • 1. 先行发生
        • 编译器重排
        • 同步机制
          • init函数
          • 协程的创建
          • channel
          • sync 包
            • 1. sync.mutex
            • 2. sync.rwmutex
            • 3. sync.once
          • atomic
        • 参考文献

概念

1. 先行发生

The happens before relation is defined as the transitive closure of the union of the sequenced before and synchronized before relations.

翻译:
happens before关系被定义为序列化before关系和同步化before关系的联合的传递闭包。
解析:

  1. happens before关系描述两个事件之间的先后顺序
  2. 序列化before关系表示单个goroutine内事件间的顺序
  3. 同步化before关系表示不同goroutine间由同步原语同步的顺序
  4. happens before关系是上述两个关系的联合
  5. 传递闭包表示如果A happens before B,B happens before C,那么A happens before C

可以理解为程序的执行顺序,在单个协程当中,程序先行发生的顺序就是程序表达的顺序
举个例子:

var a string
func hello() {
	a = "hello, world"
	print(a)
}

我们说a = "hello, world",先行发生于print(a)

var a string

func f() {
	print(a)
}
func f2() {
	a = "hello, world"
}
func hello() {
	go f()
	go f2()
}

在多个协程对共享变量的读写中,为了保证读写的正确性,我们需要引入同步机制保证程序的顺序一致性执行。比如channel,sync、atomic package。在上面的例子中f()有可能打印空字符串或者"hello, world",随机的。print(a)不先行发生于a赋值,a赋值也不先行发生于print(a),我们就说这是并发的。我们应该如何保证f2的写入一定能被f()看到呢?这就是我们要讨论的内容。

编译器重排

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

同步机制

以下是一些我们会用到的让程序保证顺序一致性执行的一些常用手段

init函数

一个函数的初始化函数可能在单个goroutine中执行,但是执行过程中有可能会开启另外一个协程并发执行,这时:
p引入包q,q的init函数结束先行发生于q的所有init函数的开始
所有的init函数执行完了才会执行main函数

协程的创建

新协程的创建先行发生与该协程的执行

var a string

func f() {
	print(a)
}
func hello() {
	a = "hello, world"
	go f()
}

这里f()一定能打印"hello, world",因为a的赋值先行发生于协程的创建,而协程的创建先行发生于函数的执行。

var a string

func f() {
	print(a)
}
func hello() {
	go f()
	a = "hello, world"
}

如果我们调整顺序

var a string

func f() {
	print(a)
}
func hello() {
	go f()
	a = "hello, world"
}

结果是随机的

PS E:\code\go\go_study\advanced\memery_model> go run memery.go
PS E:\code\go\go_study\advanced\memery_model> go run memery.go
hello, world
PS E:\code\go\go_study\advanced\memery_model> go run memery.go
hello, world
PS E:\code\go\go_study\advanced\memery_model> go run memery.go
hello, world
PS E:\code\go\go_study\advanced\memery_model> go run memery.go
hello, world
PS E:\code\go\go_study\advanced\memery_model> go run memery.go
hello, world
PS E:\code\go\go_study\advanced\memery_model> go run memery.go
PS E:\code\go\go_study\advanced\memery_model> go run memery.go
hello, world

也就是说gorountine 的退出不会保证先行发生于程序的任何事件

channel
  1. 无缓冲channel
    当我们创建的chan 不带缓冲,chan的接收先行发生于chan的发送
var c = make(chan int)
var a string

func f() {
	a = "hello, world"
	<-c
}

func main() {
	go f()
	c <- 0
	print(a)
}

<-c先行发生于c <- 0,所以a一定能打印 "hello, world"

  1. 带缓冲chanel
    当我们创建的chan 带缓冲,当缓冲未满的时候,chan的发送先行发生于chan的接收。当缓冲满了,chan的接收先行发生于chan的发送
var c = make(chan int, 10)
var a string

func f() {
	a = "hello, world"
	c <- 0
}

func main() {
	go f()
	<-c
	print(a)
}

c <- 0先行发生于<-c,所以a一定能打印 "hello, world"

容量为c的带缓冲chan的第k个接收,先行发生于第c+k个发送

var limit = make(chan int, 3)

func main() {
	for _, w := range work {
		go func(w func()) {
			limit <- 1
			w()
			<-limit
		}(w)
	}
	select{}
}

这里当w()执行耗时任务,chan队列满了,第四个任务的 w 的 <-lmit 先行发生于 limit <- 1, 这样就能保证我们队列中只有3个任务在同时执行。

sync 包
1. sync.mutex
var l sync.Mutex
var a string

func f() {
	a = "hello, world"
	l.Unlock()
}

func main() {
	l.Lock()
	go f()
	l.Lock()
	print(a)
}

For any sync.Mutex or sync.RWMutex variable l and n < m, call n of l.Unlock() is synchronized before call m of l.Lock() returns.

l执行了n次Unlock, n次Unlock先行发生于n+1次Lock操作

2. sync.rwmutex

对于同一个sync.RWMutex变量l:

  1. l.RLock() 和 l.RUnlock() 形成配对的读锁定/读解锁操作。
  2. 第n次l.RLock()发生在第n次l.RUnlock()之前。
  3. 第n次l.RUnlock() 先行发生于第n+1次l.Lock()。
  4. 读锁定和读解锁按照配对嵌套的顺序执行。
  5. 每次读解锁先行发生于下一次写锁定。
3. sync.once
var a string
var once sync.Once

func setup() {
	a = "hello, world"
}

func doprint() {
	once.Do(setup)
	print(a)
}

func twoprint() {
	go doprint()
	go doprint()
}

once 可以实现多个协程只执行一次setup代码,并且其他协程会阻塞直到setup函数返回才会继续运行下面的代码。

atomic
go
var counter int64

func worker() {
  for {
    // 原子递增计数器
    atomic.AddInt64(&counter, 1) 
  }
}

func main() {
  for i := 0; i < 10; i++ {
    go worker()
  }

  time.Sleep(time.Second)

  // 原子读取计数器
  c := atomic.LoadInt64(&counter)
  fmt.Println(c)
}

相当于每次执行前都对变量加锁了。比如我们熟悉的i++,汇编成代码其实是3条指令,他是有可能被程序中断的,而atomic 可以实现原子性操作。

参考文献

https://go.dev/ref/mem
https://www.jianshu.com/u/89aa91463068

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/42802.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【微信小程序】使用iView组件库中的icons资源

要在微信小程序中使用iView组件库中的icons资源&#xff0c;需要先下载并引入iView组件库&#xff0c;并按照iView的文档进行配置和使用。 以下是一般的使用步骤&#xff1a; 下载iView组件库的源码或使用npm安装iView。 在小程序项目的app.json文件中添加iView组件库的引入配…

PHP中常用数组排序算法

一&#xff1a;冒泡排序 1&#xff1a;算法步骤 比较相邻项的值&#xff0c;如果前者比后者大&#xff0c;交换顺序。 进行一轮比较后&#xff0c;最后一个值为最大的值。 进行下一轮比较&#xff0c;比上次少比较一项。 以此类推&#xff0c;比较剩下最后一项的时候&#…

【Linux进程】进程控制(上) {进程创建:fork的用法,fork的工作流程,写时拷贝;进程终止:3种退出情况,退出码,常见的退出方法}

一、进程创建 1.1 fork的初步认识和基本使用 在linux中fork函数是非常重要的函数&#xff0c;它从已存在进程中创建一个新进程。新进程为子进程&#xff0c;而原进程为父进程。 #include <unistd.h> pid_t fork(void);返回值&#xff1a;子进程中返回0&#xff0c;父进…

ORB-SLAM2学习笔记5之EuRoc、TUM和KITTI开源数据运行ROS版ORB-SLAM2生成轨迹

文章目录 0 引言1 数据预处理1.1 EuRoc数据1.2 TUM数据1.3 KITTI数据 2 代码修改2.1 单目2.2 双目2.3 RGB-D 3 运行ROS版ORB-SLAM23.1 单目3.2 双目3.3 RGB-D ORB-SLAM2学习笔记系列&#xff1a; 0 引言 ORB-SLAM2学习笔记1已成功编译安装ROS版本ORB-SLAM2到本地&#xff0c;本…

SQL高级教程第三章

SQL CREATE DATABASE 语句 CREATE DATABASE 语句 CREATE DATABASE 用于创建数据库。 SQL CREATE DATABASE 语法 CREATE DATABASE database_name SQL CREATE DATABASE 实例 现在我们希望创建一个名为 "my_db" 的数据库。 我们使用下面的 CREATE DATABASE 语句&…

2023云曦期中复现

目录 SIGNIN 新猫和老鼠 baby_sql SIGNIN 签到抓包 新猫和老鼠 看到反序列化 来分析一下 <?php //flag is in flag.php highlight_file(__FILE__); error_reporting(0);class mouse { public $v;public function __toString(){echo "Good. You caught the mouse:&…

5.1.tensorRT基础(2)-正确导出onnx的介绍,使得onnx问题尽量少

目录 前言1. 正确导出ONNX总结 前言 杜老师推出的 tensorRT从零起步高性能部署 课程&#xff0c;之前有看过一遍&#xff0c;但是没有做笔记&#xff0c;很多东西也忘了。这次重新撸一遍&#xff0c;顺便记记笔记。 本次课程学习 tensorRT 基础-正确导出 onnx 的介绍&#xff0…

飞书ChatGPT机器人 – 打造智能问答助手实现无障碍交流

文章目录 前言环境列表1.飞书设置2.克隆feishu-chatgpt项目3.配置config.yaml文件4.运行feishu-chatgpt项目5.安装cpolar内网穿透6.固定公网地址7.机器人权限配置8.创建版本9.创建测试企业10. 机器人测试 前言 在飞书中创建chatGPT机器人并且对话&#xff0c;在下面操作步骤中…

基于DeepFace模型设计的人脸识别软件

完整资料进入【数字空间】查看——baidu搜索"writebug" 人脸识别软件(无外部API) V2.0 基于DeepFace模型设计的人脸识别软件 V1.0 基于PCA模型设计的人脸识别软件 V2.0 更新时间&#xff1a;2018-08-15 在观看了吴恩达老师的“深度学习课程”&#xff0c;了解了深…

2023/7/23周报

目录 摘要 论文阅读 1、题目和现存问题 2、问题阐述及相关定义 3、LGDL模型框架 4、实验准备 5、实验过程 深度学习 1、GCN简单分类任务 2、文献引用数据分类案例 3、将时序型数据构建为图数据格式 总结 摘要 本周在论文阅读上&#xff0c;对基于图神经网络与深度…

【蓝牙AVDTP A2DP协议】

蓝牙AVDTP A2DP 一.AVDTP1.1 AVDTP概念1.2 Source Sink整体框架1.3 AVDTP术语1.3.2 Stream1.3.2 SRC and Sink1.3.3 INT and ACP1.3.4 SEP&#xff1a; 1.4 AVDTP体系1.4.1 体系概括1.4.2 Transport Services 1.5 Signaling Procedures1.5.1 General Requirements1.5.2 Transac…

关于Arduino IDE库文件存放路径问题总结(双版本)

在开发过程中,如果不注意,库文件存放路径很乱,如果在转移系统环境时,容易忘记备份。编译过程中出现多个可用引用包的位置,为了解决这些问题,要明白各文件夹的默认路径在哪,区别在哪,如有了解不对的地方请指正。 IDE安装目录(默认C盘,自定义可以其他盘符下)IDE升级可…

2023华为OD统一考试(B卷)题库清单(持续收录中)以及考点说明

目录 专栏导读2023 B卷 “新加题”&#xff08;100分值&#xff09;2023Q2 100分2023Q2 200分2023Q1 100分2023Q1 200分2022Q4 100分2022Q4 200分牛客练习题 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷&#xff09;》。 刷的越多&…

PALO ALTO NETWORKS 的新一代防火墙如何保护企业安全

轻松采用创新技术、阻止网络攻击得逞并专注更重要的工作 IT 的快速发展已改变网络边界的面貌。数据无处不在&#xff0c;用户可随时随地从各类设备访问这些数据。同时&#xff0c;IT 团队正在采用云、分析和自动化来加速新应用的交付以及推动业务发展。这些根本性的转变带来了…

11 简单的Thymeleaf语法

11.1 spring-boot环境准备 重要依赖&#xff1a; <!--thymeleaf--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> 11.2 转发消息不转义 就是如…

Vue3状态管理库Pinia——核心概念(Store、State、Getter、Action)

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全干发展 &#x1f4c3;个人状态&#xff1a; 研发工程师&#xff0c;现效力于中国工业软件事业 &#x1f680;人生格言&#xff1a; 积跬步…

iClient3D for CesiumWebGL入门之使用vscode以服务方式运行调试

作者&#xff1a;超图研究院技术支持中心-于丁 iClient3D for Cesium&WebGL入门之使用vscode以服务方式运行调试 相信大家第一次使用SuperMap iClient3D for Cesium或SuperMap iClient3D for WebGL的时候&#xff0c;都遇到过和我一样的事情&#xff1a; 在文件夹中直接打…

Android Studio 提示 Failed to initialize editor问题的解决

Android Studio 从2018的版本升级到2021年的版本后&#xff0c;无法预览xml。我查了很久&#xff0c;最后发现是Gradle的版本和工具不匹配&#xff0c;按照开发工具的提示&#xff0c;升级版本即可&#xff0c;我的是从3.2.1升级到了4.2.2

生产者消费者模型

生产者消费者模型 文章目录 生产者消费者模型概念原则优点 基于BlockingQueue的生产者消费者模型BlockingQueue模拟实现单生产者消费者模型基于计算任务和存储任务的生产者消费者模型 概念 生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题生产者和消费者彼…

C++ 单例模式(介绍+实现)

文章目录 一. 设计模式二. 单例模式三. 饿汉模式四. 懒汉模式结束语 一. 设计模式 单例模式是一种设计模式 设计模式(Design Pattern)是一套被反复使用&#xff0c;多数人知晓的&#xff0c;经过分类的&#xff0c;代码设计经验的总结。 为什么要有设计模式 就像人类历史发展会…