Go 学习笔记(九)- 基于共享变量的并发

并发其实在很多情况下都是不安全的,特别是写数据的时候,所以需要深入了解并发,解决他们不安全因素。

竞争条件

一个线性的程序,代码是根据流程控制的,先做什么什么,然后做什么什么,都是固定死的。
例如 js 他是单线程的,哪怕是异步,执行的时候也只是当前列队下的代码,不会同时执行的。
但是在 goroutine 中,我们没办法知道他在什么时候执行,谁先执行,执行多久。

如果在并发情况下,一个函数可以正常工作,那么这个函数就是并发安全的,他不需要额外的同步操作。
那么在非并发安全的情况下,我们就要使用互斥条件,让他们让他们安全的工作。
竞争条件指的是程序在多个goroutine交叉执行操作时,没有给出正确的结果。

例子我就不重复了,概念就是当多个 goroutine 访问同一变量其中一个是写操作的时候就会发生数据竞争。

有三种方式可以避免数据竞争:
第一种方法是不要去写变量。
第二种避免数据竞争的方法是,避免从多个goroutine访问变量。
第三种避免数据竞争的方法是允许很多goroutine去访问变量,但是在同一个时刻最多只有一个goroutine在访问。

sync.Mutex互斥锁

使用 sync.MutexLock 创建互斥锁,使用 Unlock 释放互斥
LockUnlock 之间的代码段中的内容 goroutine 可以随便读取或者修改,这个代码段叫做临界区。
通过 defer 来延迟到最后释放互斥。

sync.RWMutex读写锁

这个是 sync.Mutex 的“多读单写”版本,就是允许多个 goroutine 读取,但是多个 goroutine 写的话是互斥的。

内存同步

不是特别理解,例子运行了 N 多遍也没测试出来、
大概意思就是在不使用channel且不使用mutex这样的显式同步操作时,就没法保证事件在不同的goroutine中看到的执行顺序是一致的了。

sync.Once初始化

使用 sync.Once 代替 sync.Mutex 进行简洁的初始化操作。

竞争条件检测

go buildgo run 或者 go test 命令后面加上 -race 的 flag 来生成可检测的版本。

Goroutines和线程

线程是 cpu 级别的概念,而 goroutine 是 go 管理的。goroutine 是 go 进行动态调度的,而线程则是 cpu 直接调度的。

GOMAXPROCS 环境变量可以控制 go 使用多少个 cpu 内核进行处理。

Goroutine 没有ID号的,是为了防止被滥用。

小结

从第八章开始就晕晕乎乎的,第九章也一样,只能理解个大概,因为一直没有接触过多线程的概念一下子实在难以接受。