goroutine
が効果を発揮するのは、I/O
が絡むケース。メモリや CPU のみを使う計算処理を並行処理してもそれほど効果を発揮しない。
データ処理に順番ありの可能性がある。処理の間に 1 時間掛かっても問題ないか?と考える。
処理が行われたか、行われたかったかの 2 パターンしかない。処理を分解したときにこれ以上分解できない単位。
i++
は非アトミック。
アトミックな処理であれば複数のゴルーチンで安全に扱える。
排他処理が必要。 sync.Mutex
で解決できる。
ライブロックが起こる原因の多くは、試行回数に上限がない。 2 つの並行プロセスが互いのデッドロックを予防して起こる。
ライブロックのように 1 つ以上の貪欲なプロセスが他のリソースを奪うことで起こる。
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var wg sync.WaitGroup
var sharedLock sync.Mutex
const RUNTIME = 1 * time.Second
greedWorker := func() {
defer wg.Done()
var count int
for begin := time.Now(); time.Since(begin) <= RUNTIME; {
sharedLock.Lock()
time.Sleep(3 * time.Nanosecond)
sharedLock.Unlock()
count++
}
fmt.Printf("greed worker loops: %v\n", count)
}
politeWorker := func() {
defer wg.Done()
var count int
for begin := time.Now(); time.Since(begin) <= RUNTIME; {
sharedLock.Lock()
time.Sleep(1 * time.Nanosecond)
sharedLock.Unlock()
sharedLock.Lock()
time.Sleep(1 * time.Nanosecond)
sharedLock.Unlock()
sharedLock.Lock()
time.Sleep(1 * time.Nanosecond)
sharedLock.Unlock()
count++
}
fmt.Printf("polite worker loops: %v\n", count)
}
wg.Add(2)
go greedWorker()
go politeWorker()
wg.Wait()
}
$ go run main.go
greed worker loops: 867215
polite worker loops: 524996