MyDocs

# 並行処理の考え方

競合状態

データ処理に順番ありの可能性がある。処理の間に 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