并发 Go 程序中的共享变量 (四):内存同步

栏目: Go · 发布时间: 6年前

内容简介:本系列是阅读 “The Go Programming Language” 理解和记录。在上一小节中但是不像

本系列是阅读 “The Go Programming Language” 理解和记录。

在上一小节中 并发 Go 程序中的共享变量 (三):读写锁 ,我们在实现 Balance 方法也需要一个排他锁,不论这个排他锁是通过 channel 实现还是互斥锁实现都是可以的,在我们的例子中是通过 读写锁 实现的。

func Balance()int {
    mu.RLock()
    defer mu.RUnlock()
    return balance
}

但是不像 Deposit 方法那样需要读取 balance 并且加上 amount, Balance 只有一种操作,就是读取 Balance 并返回,所以即使有其它的 goroutine 在这中间有执行操作也不会造成什么问题。真的是这样么?

实际上我们还是需要锁,理由有二:

第一, Balance 不能在其他操作执行期间执行,比如 Withdraw 执行中的时候,实际上已经少了 balance,但是实际读取的 balance 可能还是一个旧值。

第二,同步不仅仅和 goroutine 的执行顺序相关,同步也会影响内存。

在现代计算机中,一般都会有多个 CPU,每个有 CPU 有自己的主存缓存。为了性能,写到主存的数据一般都会在每个 CPU 内部首先缓存起来,然后在必要的时候提交到主存。 这些修改的提交的顺序可能和 goroutine 的执行顺序不同 。而同步原语比如说 channel 或者互斥锁的主要目的就是让 CPU 把 buffer 的数据提交到主存中,以便其它执行在其它 CPU 上的 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
}()

正如前面文章所提到的, 两个 goroutine 并发执行,而且在没有使用互斥机制的情况下共享变量,存在 data race ,因此在看到不确定的结果时不应该感到惊讶。我们可能会看到由于代码的执行顺序的不同而有不同的输出:

y:0 x:1
x:0 y:1
x:1 y:1
y:1 x:1

这四行输出可以解释为 A1,B1,A2,B2 或者 B1,A1,A2,B2。但是有一种结果可能会让你感到吃惊:

x:0 y:0
y:0 x:0

但是现实情况是:由于 CPU 或者编译器以及其它一些因素的影响,这种结果是有可能发生的。那么这 4 句语句如何交错执行才能产生这样的结果?

在单个 goroutine 中,每个语句带来的影响可以说是严格按照他们的执行顺序而产生的,goroutine 是线性一致的(sequentially consistent)。但是在多个 goroutine 中, 如果没有显式的同步机制,比如 channel 或者互斥锁,没有办法保证 goroutine 彼此之间看到影响都是严格按照执行顺序的先后而产生的 。虽然 goroutine A 肯定是先观测到 x = 1 执行完毕之后才去读取 y 的值,但是它没有办法确保 goroutine B 对 y 的修改一定能看的到,所以 goroutine A 可能读取的还是一个 y 的旧值:0。

理解并发执行的这种尝试常常很有意思,就好像并发的结果确实是 goroutine 之间的这些语句交错执行而产生的,事实可能并不是如此,正如上面的例子展示出来的一样结果都是 0 的输出,goroutine 的执行完全由可能是 A2,A1,B2,B1。这是由于赋值语句和 Print 语句指向都是不同的变量,编译器可能会得出一个论是: 这两条语句的执行结果相互不影响从而交换了这两条语句的执行顺序 。如果这两个 goroutine 是在不同的 CPU 上执行,每个 CPU 都有自己的 cache,其中一个 goroutine 写到 cache 中的数据是不能被另一个 goroutine 中的 Print 语句看到的,直到数据被同步到主从中。

所有并发的问题都能被这些已经建立的简单模式所解决:

要么保证变量只在单个 goroutine 中使用;

要么在多个 goroutine 之间使用互斥锁。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

运营笔记

运营笔记

类延昊 / 天津人民版社 / 2016-12-1 / CNY 39.80

运营是入门浅但学问深的行当。一个入门很久的人不见得能在11年内爬到塔尖,同样一个初入龙门的人占据高位也不见得非用11年。 到底该怎么做运营?如何做运营才不至于让自己忙死累死甚至茫然不知所措?如何和用户进行有效沟通?如何把握住处于塔尖20%的核心用户?如何强敌逼阵时快速找到突破口?如何挤破头皮提高转化率? 在这本书里,类类以自己常年战斗在一线摸爬滚打的经验给予了有效而真诚的解答。一起来看看 《运营笔记》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具