update progress.md & make loop

This commit is contained in:
chai2010
2016-01-11 10:05:57 +08:00
parent 8fc11cf402
commit e74f2527fc
2 changed files with 50 additions and 50 deletions

View File

@@ -1,49 +1,49 @@
## 9.5. sync.Once初始化
如果初始化成本比大的,那么将初始化延到需要的候再去做就是一个比较好的选择。如果在程序启动的时候就去做这类的初始化的话会增加程序的启动时间并且因为执行的候可能也不需要这些变量所以实际上有一些浪费。让我们在本章早一些候看到的icons量:
如果初始化成本比大的,那麽將初始化延到需要的候再去做就是一個比較好的選擇。如果在程序啟動的時候就去做這類的初始化的話會增加程序的啟動時間併且因爲執行的候可能也不需要這些變量所以實際上有一些浪費。讓我們在本章早一些候看到的icons量:
```go
var icons map[string]image.Image
```
这个版本的Icon用到了初始化(lazy initialization)。
這個版本的Icon用到了初始化(lazy initialization)。
```go
func loadIcons() {
icons = map[string]image.Image{
"spades.png": loadIcon("spades.png"),
"hearts.png": loadIcon("hearts.png"),
"diamonds.png": loadIcon("diamonds.png"),
"clubs.png": loadIcon("clubs.png"),
}
icons = map[string]image.Image{
"spades.png": loadIcon("spades.png"),
"hearts.png": loadIcon("hearts.png"),
"diamonds.png": loadIcon("diamonds.png"),
"clubs.png": loadIcon("clubs.png"),
}
}
// NOTE: not concurrency-safe!
func Icon(name string) image.Image {
if icons == nil {
loadIcons() // one-time initialization
}
return icons[name]
if icons == nil {
loadIcons() // one-time initialization
}
return icons[name]
}
```
如果一个变量只被一个单独的goroutine所访问的话,我可以使用上面的这种模板但这种模板在Icon被并发调用时并不安全。就像前面行的那Deposit(存款)函数一样Icon函也是由多个步骤组成的:首先测试icons是否空,然load些icons后将icons更新为一个非空的值。直觉会告诉我们最差的情是loadIcons函被多次访问会带来数据竞争。当第一goroutine在忙着loading些icons的候,另一goroutine入了Icon函数,发现变量是nil后也会调用loadIcons函
如果一個變量隻被一個單獨的goroutine所訪問的話,我可以使用上面的這種模闆但這種模闆在Icon被併發調用時併不安全。就像前面行的那Deposit(存款)函數一樣Icon函也是由多個步驟組成的:首先測試icons是否空,然load些icons後將icons更新爲一個非空的值。直覺會告訴我們最差的情是loadIcons函被多次訪問會帶來數據競爭。當第一goroutine在忙着loading些icons的候,另一goroutine入了Icon函數,發現變量是nil後也會調用loadIcons函
过这种直觉是错误的。(我希望在你从现在开始能够构建自己对并发的直,也就是说对并发的直觉总是不能被信任的!)回忆一下9.4。因缺少式的同步,编译器和CPU是可以意地去更改访问内存的指令序,以任意方式,要保每一goroutine自己的执行顺序一致。其中一可能loadIcons的句重排是下面这样。它在填icons量的值之前先用一空map初始化icons量。
過這種直覺是錯誤的。(我希望在你從現在開始能夠構建自己對併發的直,也就是説對併發的直覺總是不能被信任的!)迴憶一下9.4。因缺少式的同步,編譯器和CPU是可以意地去更改訪問內存的指令序,以任意方式,要保每一goroutine自己的執行順序一致。其中一可能loadIcons的句重排是下面這樣。它在填icons量的值之前先用一空map初始化icons量。
```go
func loadIcons() {
icons = make(map[string]image.Image)
icons["spades.png"] = loadIcon("spades.png")
icons["hearts.png"] = loadIcon("hearts.png")
icons["diamonds.png"] = loadIcon("diamonds.png")
icons["clubs.png"] = loadIcon("clubs.png")
icons = make(map[string]image.Image)
icons["spades.png"] = loadIcon("spades.png")
icons["hearts.png"] = loadIcon("hearts.png")
icons["diamonds.png"] = loadIcon("diamonds.png")
icons["clubs.png"] = loadIcon("clubs.png")
}
```
因此,一goroutine在检查icons是非空,也不能就假设这个变量的初始化流程已走完了(译注:可能是塞了空map里面的值还没填完,也就是填值的句都没执行完呢)。
因此,一goroutine在檢査icons是非空,也不能就假設這個變量的初始化流程已走完了(譯註:可能是塞了空map里面的值還沒填完,也就是填值的句都沒執行完呢)。
简单且正的保所有goroutine能够观察到loadIcons效果的方式是用一mutex同步检查
簡單且正的保所有goroutine能夠觀察到loadIcons效果的方式是用一mutex同步檢査
```go
var mu sync.Mutex // guards icons
@@ -51,56 +51,56 @@ var icons map[string]image.Image
// Concurrency-safe.
func Icon(name string) image.Image {
mu.Lock()
defer mu.Unlock()
if icons == nil {
loadIcons()
}
return icons[name]
mu.Lock()
defer mu.Unlock()
if icons == nil {
loadIcons()
}
return icons[name]
}
```
然而使用互斥访问icons的代就是没有办法对该变量进行并发访问,即使变量已被初始化完且再也不会进行变动。这里我可以引入一个允许多读的锁
然而使用互斥訪問icons的代就是沒有辦法對該變量進行併發訪問,卽使變量已被初始化完且再也不會進行變動。這里我可以引入一個允許多讀的鎖
```go
var mu sync.RWMutex // guards icons
var icons map[string]image.Image
// Concurrency-safe.
func Icon(name string) image.Image {
mu.RLock()
if icons != nil {
icon := icons[name]
mu.RUnlock()
return icon
}
mu.RUnlock()
mu.RLock()
if icons != nil {
icon := icons[name]
mu.RUnlock()
return icon
}
mu.RUnlock()
// acquire an exclusive lock
mu.Lock()
if icons == nil { // NOTE: must recheck for nil
loadIcons()
}
icon := icons[name]
mu.Unlock()
return icon
// acquire an exclusive lock
mu.Lock()
if icons == nil { // NOTE: must recheck for nil
loadIcons()
}
icon := icons[name]
mu.Unlock()
return icon
}
```
上面的代码有两个临界区。goroutine首先会获取一个写锁,查询map后释放锁。如果目被找到了(一般情下),那么会直接返。如果有找到那goroutine会获取一个写锁。不放共享锁的话,也有任何办法来将一个共享锁升级为一个互斥,所以我们必须重新检查icons量是否nil以防止在执行这一段代码的时icons量已被其它gorouine初始化了。
上面的代碼有兩個臨界區。goroutine首先會獲取一個寫鎖,査詢map後釋放鎖。如果目被找到了(一般情下),那麽會直接返。如果有找到那goroutine會獲取一個寫鎖。不放共享鎖的話,也有任何辦法來將一個共享鎖陞級爲一個互斥,所以我們必鬚重新檢査icons量是否nil以防止在執行這一段代碼的時icons量已被其它gorouine初始化了。
上面的模使我的程序能更好的并发,但是有一点太复杂且容易出。幸的是sync包为我们提供了一个专门的方案来解决这种一次性初始化的问题sync.Once。概念上来讲,一次性的初始化需要一互斥量mutex和一boolean变量来记录初始化是不是已完成了;互斥量用来保护boolean量和客户端数据结构。Do这个唯一的方法需要接收初始化函数作为其参数。让我们用sync.Once来简化前面的Icon函吧:
上面的模使我的程序能更好的併發,但是有一點太複雜且容易出。幸的是sync包爲我們提供了一個專門的方案來解決這種一次性初始化的問題sync.Once。概念上來講,一次性的初始化需要一互斥量mutex和一boolean變量來記録初始化是不是已完成了;互斥量用來保護boolean量和客戶端數據結構。Do這個唯一的方法需要接收初始化函數作爲其參數。讓我們用sync.Once來簡化前面的Icon函吧:
```go
var loadIconsOnce sync.Once
var icons map[string]image.Image
// Concurrency-safe.
func Icon(name string) image.Image {
loadIconsOnce.Do(loadIcons)
return icons[name]
loadIconsOnce.Do(loadIcons)
return icons[name]
}
```
每一次Do(loadIcons)的用都会锁定mutex并会检查boolean量。在第一次调用时,变量的值是falseDo会调用loadIcons并会将boolean设置为true。随后的调用什都不但是mutex同步会保证loadIcons对内存(里其就是指icons量啦)生的效果能够对所有goroutine可。用这种方式使用sync.Once的,我们能够避免在量被建完成之前和其它goroutine共享该变量。
每一次Do(loadIcons)的調用都會鎖定mutex併會檢査boolean量。在第一次調用時,變量的值是falseDo會調用loadIcons併會將boolean設置爲true。隨後的調用什都不但是mutex同步會保證loadIcons對內存(里其就是指icons量啦)生的效果能夠對所有goroutine可。用這種方式使用sync.Once的,我們能夠避免在量被建完成之前和其它goroutine共享該變量。
练习 9.2:2.6.2中的PopCount的例子使用sync.Once在第一次需要用到的时候进行初始化。(虽然实际上,PopCount这样很小且高度化的函数进行同步可能代价没法接受)
**練習 9.2**2.6.2中的PopCount的例子使用sync.Once在第一次需要用到的時候進行初始化。(雖然實際上,PopCount這樣很小且高度化的函數進行同步可能代價沒法接受)

View File

@@ -82,7 +82,7 @@
- [ ] 9.2 Mutual Exclusion: sync.Mutex
- [x] 9.3 Read/Write Mutexes: sync.RWMutex
- [x] 9.4 Memory Synchronization
- [ ] 9.5 Lazy Initialization: sync.Once
- [x] 9.5 Lazy Initialization: sync.Once
- [x] 9.6 The Race Detector
- [ ] 9.7 Example: Concurrent Non-Blocking Cache
- [x] 9.8 Goroutines and Threads