并发其实在很多情况下都是不安全的,特别是写数据的时候,所以需要深入了解并发,解决他们不安全因素。
一个线性的程序,代码是根据流程控制的,先做什么什么,然后做什么什么,都是固定死的。
例如 js 他是单线程的,哪怕是异步,执行的时候也只是当前列队下的代码,不会同时执行的。
但是在 goroutine 中,我们没办法知道他在什么时候执行,谁先执行,执行多久。
如果在并发情况下,一个函数可以正常工作,那么这个函数就是并发安全的,他不需要额外的同步操作。
那么在非并发安全的情况下,我们就要使用互斥条件,让他们让他们安全的工作。
竞争条件指的是程序在多个goroutine交叉执行操作时,没有给出正确的结果。
例子我就不重复了,概念就是当多个 goroutine 访问同一变量其中一个是写操作的时候就会发生数据竞争。
有三种方式可以避免数据竞争:
第一种方法是不要去写变量。
第二种避免数据竞争的方法是,避免从多个goroutine访问变量。
第三种避免数据竞争的方法是允许很多goroutine去访问变量,但是在同一个时刻最多只有一个goroutine在访问。
使用 sync.Mutex
的 Lock
创建互斥锁,使用 Unlock
释放互斥
在 Lock
和 Unlock
之间的代码段中的内容 goroutine 可以随便读取或者修改,这个代码段叫做临界区。
通过 defer
来延迟到最后释放互斥。
这个是 sync.Mutex
的“多读单写”版本,就是允许多个 goroutine 读取,但是多个 goroutine 写的话是互斥的。
不是特别理解,例子运行了 N 多遍也没测试出来、大概意思就是在不使用channel且不使用mutex这样的显式同步操作时,就没法保证事件在不同的goroutine中看到的执行顺序是一致的了。
使用 sync.Once
代替 sync.Mutex
进行简洁的初始化操作。
在go build,go run或者go test命令后面加上 -race
的flag来生成可检测的版本。
线程是 cpu 级别的概念,而 goroutine 是 go 管理的。不知道我理解的对不对。goroutine 是又 go 进行动态调度的,而线程则是 cpu 直接调度的。
GOMAXPROCS 环境变量可以控制 go 使用多少个 cpu 内核进行处理。
Goroutine没有ID号的,为了防止被滥用。
从第八章开始,就晕晕乎乎的,第九章也一样,只能理解个大概,因为一直没有接触过多线程的概念,一下子实在难以接受。