NIUCLOUD是一款SaaS管理后台框架多应用插件+云编译。上千名开发者、服务商正在积极拥抱开发者生态。欢迎开发者们免费入驻。一起助力发展! 广告
## 内存同步 对于一个共享变量的并发操作,写加锁可以理解,是为了避免数据竞争.那么为什么读的时候也要加锁呢? 1. Balance不会在其它操作比如Withdraw“中间”执行。 2. 是“同步”不仅仅是一堆goroutine执行顺序的问题,同样也会涉及到内存的问题。 ## 现代计算机构造 现在很多计算是多核的,而多个协程是可能会运行在不同核心上面的,而每个核心都是有本地缓存的,为了提升效率,对内存的写入一般会在每一个处理器中缓冲,并在必要时一起flush到主存. **在一个独立的goroutine中,每一个语句的执行顺序是可以被保证的**(我理解这里的顺序是指没有共享变量的情况下),也就是说goroutine内顺序是连贯的。但是在不使用channel且不使用mutex这样的显式同步操作时,我们就没法保证事件在不同的goroutine中看到的执行顺序是一致的了。 ## 例子 ~~~ var x, y int go func() { x = 1 // A1 fmt.Print("y:", y, " ") // A2 }() go func() { y = 1 // B1 fmt.Print("x:", x, " ") // B2 }() ~~~ 这里的逻辑我们捋一下,加入第一个协程先执行了,那么第二个协程肯定能打印出x=1,假如第二个协程先执行了,第一个协程肯定能打印出y=1. x,y都打印出0的可能只有: **因为赋值和打印指向不同的变量,编译器可能会断定两条语句的顺序不会影响执行结果,并且会交换两个语句的执行顺序。如果两个goroutine在不同的CPU上执行,每一个核心有自己的缓存,这样一个goroutine的写入对于其它goroutine的Print,在主存同步之前就是不可见的了。** 有可能会打印出: ~~~ x:0 y:0 y:0 x:0 ~~~ 也就是说在一个协程中,赋值操作,在print之后去执行了.