同期プリミティブ
Go 言語の同期プリミティブ:Mutex、WaitGroup、Atomic。
WaitGroups
起動したすべての Goroutines の完了を待つために使用。
goroutine からデータを返す必要がない場合は、WaitGroups を使用。
- 複数の goroutine が互いを待つことを可能にする同期プリミティブ
- 内部カウンタが 0 になるまで実行をブロック
- package
syncが基本同期機能を提供
import "sync"
var wg sync.WaitGroup
wg.Add(int)
wg.Wait()
wg.Done()WaitGroup を関数に明示的に渡す場合は、ポインタで渡すべき。
Methods
Add:待つ goroutine の数を設定(カウンタを増加)
- 引数の整数がカウンタとして機能
- 待つ goroutine の数を示す
Wait:内部カウンタが 0 になるまで実行をブロック
Done:
Add()メソッドの内部カウントを 1 減少- goroutine が完了したことを示すために呼び出す

完全な例
func main() {
var wg sync.WaitGroup
for i := 1; i <= 3; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
fmt.Printf("Worker %d done\n", id)
}(i)
}
wg.Wait()
fmt.Println("All workers completed")
}Mutexes
ミューテックス、共有リソースを保護し、同時に 1 つの goroutine だけがアクセスできることを保証。
sync.Mutex
type Counter struct {
mu sync.Mutex
value int
}
func (c *Counter) Increment() {
c.mu.Lock()
defer c.mu.Unlock()
c.value++
}
func (c *Counter) Value() int {
c.mu.Lock()
defer c.mu.Unlock()
return c.value
}sync.RWMutex
読み書きロック、複数の読み取りを同時に許可するが、書き込みは排他的。
| メソッド | 説明 |
|---|---|
RLock() | 読み取りロックを取得(複数同時保持可能) |
RUnlock() | 読み取りロックを解放 |
Lock() | 書き込みロックを取得(排他的) |
Unlock() | 書き込みロックを解放 |
type SafeMap struct {
mu sync.RWMutex
data map[string]int
}
func (m *SafeMap) Get(key string) (int, bool) {
m.mu.RLock()
defer m.mu.RUnlock()
val, ok := m.data[key]
return val, ok
}
func (m *SafeMap) Set(key string, value int) {
m.mu.Lock()
defer m.mu.Unlock()
m.data[key] = value
}Atomic Counters
アトミックカウンタ、ロックフリーのカウント操作に使用、Mutex より軽量。
import "sync/atomic"
var counter int64
// アトミックに増加
atomic.AddInt64(&counter, 1)
// アトミックに読み取り
value := atomic.LoadInt64(&counter)
// アトミックに保存
atomic.StoreInt64(&counter, 100)
// Compare-And-Swap
atomic.CompareAndSwapInt64(&counter, old, new)完全な例
func main() {
var counter int64
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
atomic.AddInt64(&counter, 1)
}()
}
wg.Wait()
fmt.Println("Counter:", counter) // 1000
}sync.Once
関数が 1 回だけ実行されることを保証、シングルトンパターンや初期化によく使われる。
var (
instance *Database
once sync.Once
)
func GetInstance() *Database {
once.Do(func() {
instance = &Database{
// 初期化...
}
})
return instance
}sync.Pool
オブジェクトプール、一時オブジェクトを再利用して、メモリ割り当てと GC の負荷を軽減。
var bufferPool = sync.Pool{
New: func() interface{} {
return new(bytes.Buffer)
},
}
func process(data []byte) {
buf := bufferPool.Get().(*bytes.Buffer)
defer func() {
buf.Reset()
bufferPool.Put(buf)
}()
buf.Write(data)
// buffer を処理...
}sync.Map
並行安全な map、読み取りが多く書き込みが少ないシナリオに適している。
var m sync.Map
// 保存
m.Store("key", "value")
// 読み取り
value, ok := m.Load("key")
// 読み取りまたは保存(存在しない場合は保存)
actual, loaded := m.LoadOrStore("key", "default")
// 削除
m.Delete("key")
// 反復
m.Range(func(key, value interface{}) bool {
fmt.Printf("%v: %v\n", key, value)
return true // false を返すと反復を停止
})ほとんどの場合、
map + sync.RWMutex の方がパフォーマンスが良い。sync.Map は key 集合が安定していて読み取りが頻繁なシナリオに適している。Worker Pools
ワーカープールパターン、並行タスクを管理し、並行数を制御。
func worker(id int, jobs <-chan int, results chan<- int) {
for job := range jobs {
fmt.Printf("Worker %d processing job %d\n", id, job)
results <- job * 2
}
}
func main() {
const numJobs = 10
const numWorkers = 3
jobs := make(chan int, numJobs)
results := make(chan int, numJobs)
// ワーカーを起動
for w := 1; w <= numWorkers; w++ {
go worker(w, jobs, results)
}
// タスクを送信
for j := 1; j <= numJobs; j++ {
jobs <- j
}
close(jobs)
// 結果を収集
for r := 1; r <= numJobs; r++ {
<-results
}
}- Links: