第22回横浜Go読書会に参加
go言語に関する書籍を読む会です。今回から本が新しくなり『Go言語による並行処理』を読んで行きます。
- 作者: Katherine Cox-Buday,山口能迪
- 出版社/メーカー: オライリージャパン
- 発売日: 2018/10/26
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
初回なので参加者の方が多かったです(初参加の方も)
yokohama-go-reading.connpass.com
誤植など
epub版固有の誤植というのがいくつか見つかりました。
ちなみに電子版で呼んでる方はpdfが多い気がしますが、私はepub派です。
組み版や翻訳、原著との差分に関して気になる点
柴田さんをはじめ、複数の方から意見が出ました。
「驚異的並列」の元の単語(embarrassingly parallel)を書いて欲しい。
- 対応する原文の単語をよく併記している(by柴田さん)
原著ではソースに行番号が書かれている。
- p5では3行目、5行目と本文中で参照されているので消すべきではないと思うが...
var data int go func() { // ❶ data++ }() if data == 0 { fmt.Printf("the value is %v.\n", data) }
ここで、3 行目と 5 行目ではともに dataという変数にアクセスしようとしています。
- 組み版が気になる。原著ではソースがまたがっている(p5-6)が、これは無理やり改ページした後にソースを掲載するべき
並列性と並行性
- Goでは並列処理がかけない。並行処理だけ。並列処理ができる言語はないような気がする。
原著では並行性は Concurrency ,並列性は Parallelism になっているはず
- 確認したところそのようになってた。
アムダールの法則:並列化による性能向上
Spigotアルゴリズム:円周率の計算を並列化する方法
CPUのクロックは長いこと3,4Ghzで止まっている。
2010年頃は10GHzの時代が来る!とか言ってた時代もあった- 2000年前後だそうです
競合状態(race condition)
- 競合状態の検出は困難。
並列性を実現するのはシステムコールでやる。
- あるプロセスを特定のCPUに割り当てるシステムコールが存在する。
過去にはシングルコアで動かないプログラムをマルチコアCPUで動かすと、想定していなかった競合に関するバグが発生した。
golangのデッドロック検出機能
type value struct { mu sync.Mutex value int } var wg sync.WaitGroup printSum := func(v1, v2 *value) { defer wg.Done() v1.mu.Lock() // ❶ defer v1.mu.Unlock() // ❷ time.Sleep(2 * time.Second) // ❸ v2.mu.Lock() defer v2.mu.Unlock() fmt.Printf("sum=%v\n", v1.value+v2.value) } var a, b value wg.Add(2) go printSum(&a, &b) go printSum(&b, &a) wg.Wait()
$ go run sample.go fatal error: all goroutines are asleep - deadlock! goroutine 1 [semacquire]: sync.runtime_Semacquire(0xc000014054) /usr/local/go/src/runtime/sema.go:56 +0x39 sync.(*WaitGroup).Wait(0xc000014054) /usr/local/go/src/sync/waitgroup.go:130 +0x64 main.main() /Users/yuyabu/Documents/Study/go/chap1/training2/training2.go:34 +0x122 goroutine 5 [semacquire]: sync.runtime_SemacquireMutex(0xc000014074, 0x0) /usr/local/go/src/runtime/sema.go:71 +0x3d sync.(*Mutex).Lock(0xc000014070) /usr/local/go/src/sync/mutex.go:134 +0xff main.main.func1(0xc000014060, 0xc000014070) /Users/yuyabu/Documents/Study/go/chap1/training2/training2.go:24 +0xa1 created by main.main /Users/yuyabu/Documents/Study/go/chap1/training2/training2.go:32 +0xea goroutine 6 [semacquire]: sync.runtime_SemacquireMutex(0xc000014064, 0x0) /usr/local/go/src/runtime/sema.go:71 +0x3d sync.(*Mutex).Lock(0xc000014060) /usr/local/go/src/sync/mutex.go:134 +0xff main.main.func1(0xc000014070, 0xc000014060) /Users/yuyabu/Documents/Study/go/chap1/training2/training2.go:24 +0xa1 created by main.main /Users/yuyabu/Documents/Study/go/chap1/training2/training2.go:33 +0x114 exit status 2
上記のプログラムはsleep関数を抜くとdeadlockが発生しにくくなる。
複数のロックを取るときは、同じ順番で取ることが重要。
- Edgar Coffmanのdeadlock検知の論文が挙げられている
golangで1ミリ秒ごとにxxするという書き方
golangでこの構文は何か?という話題になった。
for range time.Tick(1 * time.Millisecond) { cadence.Broadcast() }
- 1ミリ秒待ってブロードキャスト、という意味になる。
- 無限ループの先頭にsleep()関数を使うことでもかけるが、チャネルを使ってこのように書くこともできる
- 上記ソースのtime.Tickのチャネルは閉じなくていいのか?
- 残るけど、mainが死んだ時に全員死ぬのでOK
condとmutexの違い
- condは外部からbroadcast,signalで起こすことができる。
- broadcastとsignalの違いは全てのcondを起こせるか、どうかの違いがある。
cadence := sync.NewCond(&sync.Mutex{}) go func() { for range time.Tick(1 * time.Millisecond) { cadence.Broadcast() //broadcastは全てが起きる // } }() takeStep := func() { cadence.L.Lock() cadence.Wait() //waitじにはロックを解除する。、waitを抜けたときはlockを獲得している。 //ブロードキャスト、もしくはシグナルの受診時にロックを獲得した状態でwait()のいちに戻ってくる //なお復帰、ロック再獲得はatomicに実行される cadence.L.Unlock() }
他
- Juliaは言語仕様にchanelがあるらしい
次回は2.3「これがどう役に立つのか」(p.29)から