先看代码
package main import ( "fmt" "runtime" "sync" "time" ) func main() { //runtime.GOMAXPROCS(1)//只使用一个cpu,让所有的groutine运行在一个线程上面注释这句话会发生数据争用 var wg sync.WaitGroup var a = 1 for i := 0; i < 10; i++ { wg.Add(1) go func() { a++ wg.Done() }() } a = 3 wg.Wait() fmt.Println(a) time.Sleep(60 * time.Second) }
数据争用是什么?
①多个线程对于同一个变量、
②同时地、
③进行读/写操作的现象并且
④至少有一个线程进行写操作。
(也就是说,如果所有线程都是只进行读操作,那么将不构成数据争用)后果:如果发生了数据争用,读取该变量时得到的值将变得不可知,使得该多线程程序的运行结果将完全不可预测,可能直接崩溃。
如何检测程序是否出现数据争用
加参数 -racego run -race main.go
- 为什么会出现数据争用?
每一条语句执行在汇编层面上看非原子操作,一个线程在修改一个全局变量的时候,另一个线程也可能在修改它,修改发生冲突 如何解决?
加各种锁,还有避免使用全局变量package main import ( "fmt" "time" ) func main() { for i := 0; i < 10; i++ { go func() { c := add(1, 2) fmt.Println(c) }() } time.Sleep(60 * time.Second) } func add(a, b int) (c int) { c = a + b return }
上诉代码没有出现数据争用,原因是没有共用一个变量
总结
我这里引用别人的总结
内存重排是指程序在实际运行时对内存的访问顺序和代码编写时的顺序不一致,主要是为了提高运行效率。分别是硬件层面的 CPU 重排 和软件层面的 编译器重排。
个人理解:(内存重重排的主要依据还是编译器会根据语句的执行时间合理分配执行顺序)单线程的程序一般不会有太大问题;多线程情况下,有时会出现诡异的现象,解决办法就是使用标准库里的锁。锁会带来性能问题,为了降低影响,锁应该尽量减小粒度,并且不要在互斥区(锁住的代码)放入耗时长的操作。