モジュールのレッスン (4/5)
`sync.Mutex` と `sync.WaitGroup`
チャネルは Go の主要なイディオムですが、より伝統的な場合もあります
プリミティブが必要です。 sync パッケージはミューテックスを提供します。お待ちください。
グループやその他のユーティリティを使用して共有状態を調整します。
sync.Mutex: 排他ロック
import "sync"
type Counter struct {
mu sync.Mutex
count int
}
func (c *Counter) Inc() {
c.mu.Lock()
defer c.mu.Unlock()
c.count++
}
func (c *Counter) Get() int {
c.mu.Lock()
defer c.mu.Unlock()
return c.count
}Lock() と Unlock() の間に入るゴルーチンは一度に 1 つ だけです。
必須パターン: Lock() の直後の defer mu.Unlock()。
sync.RWMutex: 多数のリーダー、1 つのライター
読み取りが多い状態の場合:
var mu sync.RWMutex
mu.RLock() // read lock: più goroutine simultanee OK
v := data
mu.RUnlock()
mu.Lock() // write lock: esclusivo
data = newValue
mu.Unlock()読み取りが書き込みよりも実際に優勢な場合にのみ役立ちます。それ以外の場合はレギュラー
sync.Mutex はよりシンプルで、内部の処理能力が低いため、多くの場合高速です。
論争。
sync.WaitGroup: N 個のゴルーチンを待機します
var wg sync.WaitGroup
for i := 0; i < 3; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
work(id)
}(i)
}
wg.Wait() // blocca finché tutti i Done() sono arrivatiワークフロー:
wg.Add(N)は、(ゴルーチンを起動する前に) カウンタを N ずつインクリメントします。- 各ゴルーチンは、終了時に
wg.Done()を呼び出します (理想的にはdefer経由)。 wg.Wait()は、カウンターが 0 に戻るまでブロックします。
sync.Once: スレッドセーフな遅延初期化
var (
once sync.Once
config *Config
)
func GetConfig() *Config {
once.Do(func() {
config = loadConfig()
})
return config
}once.Do のコールバックは、同時実行下でも 1 回だけ実行されます。
呼び出します。シングルトン/遅延初期化パターン。
ミューテックスとチャネル: いつ何を使用するか
ガイドライン (Go の公式 FAQ からも):
| ケース | 推奨ツール |
|---|---|
| データの所有権の譲渡 | チャンネル |
| 作業の分散 (ワークキュー) | チャンネル |
| 独立したゴルーチンを調整する | チャンネル |
| 構造体フィールドの保護 | ミューテックス |
| キャッシュ/カウンター | ミューテックス またはアトミック |
| シングルトンリファレンス | sync.Once |
「オーケストレーションするチャネル、共有データのミューテックス」は良い経験則です。
レースディテクター
-race を使用してテストを実行し、データ競合を特定します。
go test -race ./...
go run -race main.go競合検出器はバイナリを計測し、すべての非同期をログに記録します。 共有メモリへのアクセス。同時実行コードを検証するためのツール #1 生産前。
試してみてください
mu.Lock() + defer mu.Unlock() でカウントの増分を保護します。
ヒントを表示
Lock() の直後に mu.Unlock() を遅延させると、パニック時でも解放が保証されます。
3 回の試行後に解決策が利用可能になります
3 つのゴルーチンを起動し、sync.WaitGroup (各ゴルーチンの Add(3)、Done()、Wait()) を使用してすべてのゴルーチンを待ちます。
ヒントを表示
goroutine を起動する前に追加します。 defer を使用して goroutine 内で実行されます。
3 回の試行後に解決策が利用可能になります
ミューテックスが確実に解放されるように推奨されるパターンは何ですか?
mu.Lock()
// ?要約
sync.Mutexは共有状態を保護します。常にdefer mu.Unlock()。- Use a pointer receiver to avoid copying the mutex.
sync.RWMutexfor many-readers/one-writer patterns (only if it helps).sync.WaitGroup:goの前のAdd、Doneとdefer、Waitを待機します。sync.Once: thread-safe lazy initialization.- "Channels to orchestrate, mutexes for shared data".
go test -race: tool #1 to spot data races.