make loop

This commit is contained in:
chai2010 2016-01-08 18:15:06 +08:00
parent d974fbca91
commit 8d5ac95935

View File

@ -1,10 +1,10 @@
## 9.4. 內存同步 ## 9.4. 內存同步
你可能比较纠结为什么Balance方法需要用到互斥条件无论是基于channel还是基于互斥量。毕竟和存款不一样它只由一个简单的操作组成所以不会碰到其它goroutine在其执行"中"执行其它的逻辑的风险。这里使用mutex有两方面考虑。第一Balance不会在其它操作比如Withdraw“中间”执行。第二(更重要)的是"同步"不仅仅是一堆goroutine执行顺序的问题同样也会涉及到内存的问题 你可能比較糾結爲什麽Balance方法需要用到互斥條件無論是基於channel還是基於互斥量。畢竟和存款不一樣它隻由一個簡單的操作組成所以不會碰到其它goroutine在其執行"中"執行其它的邏輯的風險。這里使用mutex有兩方面考慮。第一Balance不會在其它操作比如Withdraw“中間”執行。第二(更重要)的是"同步"不僅僅是一堆goroutine執行順序的問題同樣也會涉及到內存的問題
现代计算机中可能会有一堆处理器,每一个都会有其本地缓存(local cache)。为了效率对内存的写入一般会在每一个处理器中缓冲并在必要时一起flush到主存。这种情况下这些数据可能会以与当初goroutine写入顺序不同的顺序被提交到主存。像channel通信或者互斥量操作这样的原语会使处理器将其聚集的写入flush并commit这样goroutine在某个时间点上的执行结果才能被其它处理器上运行的goroutine得到。 現代計算機中可能會有一堆處理器,每一個都會有其本地緩存(local cache)。爲了效率對內存的寫入一般會在每一個處理器中緩衝併在必要時一起flush到主存。這種情況下這些數據可能會以與當初goroutine寫入順序不同的順序被提交到主存。像channel通信或者互斥量操作這樣的原語會使處理器將其聚集的寫入flush併commit這樣goroutine在某個時間點上的執行結果才能被其它處理器上運行的goroutine得到。
虑一下下面代码片段的可能输出: 慮一下下面代碼片段的可能輸出:
```go ```go
var x, y int var x, y int
@ -18,7 +18,7 @@ go func() {
}() }()
``` ```
为两个goroutine是并发执行并且访问共享变量时也没有互斥会有数据竞争所以程序的运行结果没法预测的话也请不要惊讶。我们可能希望它能够打印出下面这四种结果中的一种相当于几种不同的交错执行时的情况 爲兩個goroutine是併發執行併且訪問共享變量時也沒有互斥會有數據競爭所以程序的運行結果沒法預測的話也請不要驚訝。我們可能希望它能夠打印出下面這四種結果中的一種相當於幾種不同的交錯執行時的情況
``` ```
y:0 x:1 y:0 x:1
@ -27,19 +27,19 @@ x:1 y:1
y:1 x:1 y:1 x:1
``` ```
第四行可以被解释为执行顺序A1,B1,A2,B2或者B1,A1,A2,B2的执行结果。 第四行可以被解釋爲執行順序A1,B1,A2,B2或者B1,A1,A2,B2的執行結果。
然而实际的运行时还是有些情况让我们有点惊讶 然而實際的運行時還是有些情況讓我們有點驚訝
``` ```
x:0 y:0 x:0 y:0
y:0 x:0 y:0 x:0
``` ```
但是根据所使用的编译器CPU或者其它很多影响因子这两种情况也是有可能发生的。那么这两种情况要怎么解释呢? 但是根據所使用的編譯器CPU或者其它很多影響因子這兩種情況也是有可能發生的。那麽這兩種情況要怎麽解釋呢?
在一个独立的goroutine中每一个语句的执行顺序是可以被保证的也就是说goroutine是顺序连贯的。但是在不使用channel且不使用mutex这样的显式同步操作时我们就没法保证事件在不同的goroutine中看到的执行顺序是一致的了。尽管goroutine A中一定需要观察到x=1执行成功之后才会去读取y但它没法确保自己观察得到goroutine B中对y的写入所以A还可能会打印出y的一个旧版的值。 在一個獨立的goroutine中每一個語句的執行順序是可以被保證的也就是説goroutine是順序連貫的。但是在不使用channel且不使用mutex這樣的顯式同步操作時我們就沒法保證事件在不同的goroutine中看到的執行順序是一致的了。盡管goroutine A中一定需要觀察到x=1執行成功之後才會去讀取y但它沒法確保自己觀察得到goroutine B中對y的寫入所以A還可能會打印出y的一個舊版的值。
尽管去理解并发的一种尝试是去将其运行理解为不同goroutine语句的交错执行但看看上面的例子这已经不是现代的编译器和cpu的工作方式了。因为赋值和打印指向不同的变量编译器可能会断定两条语句的顺序不会影响执行结果并且会交换两个语句的执行顺序。如果两个goroutine在不同的CPU上执行每一个核心有自己的缓存这样一个goroutine的写入对于其它goroutine的Print在主存同步之前就是不可见的了。 盡管去理解併發的一種嚐試是去將其運行理解爲不同goroutine語句的交錯執行但看看上面的例子這已經不是現代的編譯器和cpu的工作方式了。因爲賦值和打印指向不同的變量編譯器可能會斷定兩條語句的順序不會影響執行結果併且會交換兩個語句的執行順序。如果兩個goroutine在不同的CPU上執行每一個核心有自己的緩存這樣一個goroutine的寫入對於其它goroutine的Print在主存同步之前就是不可見的了。
所有并发的问题都可以用一致的、简单的既定的模式来规避。所以可能的话将变量限定在goroutine内部如果是多个goroutine都需要访问的变量使用互斥条件来访问 所有併發的問題都可以用一致的、簡單的旣定的模式來規避。所以可能的話將變量限定在goroutine內部如果是多個goroutine都需要訪問的變量使用互斥條件來訪問