Sync
The sync package in Go provides synchronization primitives that are commonly needed in concurrent programming. It includes tools for mutual exclusion (mutexes), wait groups, once execution, and other synchronization needs. Here’s an overview of some of the key components and their use cases:
1. Mutex (sync.Mutex)
A Mutex is used to ensure that only one goroutine can access a critical section of code at a time. It is useful for protecting shared resources.
Example:
package main
import (
"fmt"
"sync"
)
var (
counter int
mu sync.Mutex
)
func increment(wg *sync.WaitGroup) {
defer wg.Done()
mu.Lock()
counter++
mu.Unlock()
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go increment(&wg)
}
wg.Wait()
fmt.Println("Counter:", counter)
}2. RWMutex (sync.RWMutex)
An RWMutex is a reader/writer mutual exclusion lock. It allows multiple readers to hold the lock simultaneously but only one writer at a time. This is useful when you have a resource that is read frequently but written infrequently.
Example:
package main
import (
"fmt"
"sync"
"time"
)
var (
counter int
rwMu sync.RWMutex
)
func read(wg *sync.WaitGroup) {
defer wg.Done()
rwMu.RLock()
defer rwMu.RUnlock()
fmt.Println("Read counter:", counter)
}
func write(wg *sync.WaitGroup) {
defer wg.Done()
rwMu.Lock()
counter++
rwMu.Unlock()
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 3; i++ {
wg.Add(1)
go write(&wg)
}
for i := 0; i < 5; i++ {
wg.Add(1)
go read(&wg)
}
wg.Wait()
}3. WaitGroup (sync.WaitGroup)
A WaitGroup waits for a collection of goroutines to finish executing. It is useful for ensuring that a set of concurrent tasks have completed before proceeding.
Example:
package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done()
fmt.Printf("Worker %d starting\n", id)
time.Sleep(time.Second)
fmt.Printf("Worker %d done\n", id)
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 5; i++ {
wg.Add(1)
go worker(i, &wg)
}
wg.Wait()
fmt.Println("All workers completed")
}4. Once (sync.Once)
A Once ensures that a piece of code is executed only once, even if called from multiple goroutines.
Example:
package main
import (
"fmt"
"sync"
)
var once sync.Once
func initialize() {
fmt.Println("Initializing...")
}
func worker(wg *sync.WaitGroup) {
defer wg.Done()
once.Do(initialize)
fmt.Println("Worker done")
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 3; i++ {
wg.Add(1)
go worker(&wg)
}
wg.Wait()
}5. Cond (sync.Cond)
A Cond is used for waiting and signaling between goroutines. It allows goroutines to wait for some condition to be met.
Example:
package main
import (
"fmt"
"sync"
"time"
)
var (
sharedRsc = false
mu sync.Mutex
cond = sync.NewCond(&mu)
)
func waitForResource(id int, wg *sync.WaitGroup) {
defer wg.Done()
cond.L.Lock()
for !sharedRsc {
cond.Wait()
}
cond.L.Unlock()
fmt.Printf("Goroutine %d proceeding\n", id)
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 3; i++ {
wg.Add(1)
go waitForResource(i, &wg)
}
time.Sleep(1 * time.Second)
cond.L.Lock()
sharedRsc = true
cond.L.Unlock()
cond.Broadcast()
wg.Wait()
}These are just a few of the many use cases for the sync package in Go. It is a powerful tool for handling concurrency and ensuring that your programs run correctly and efficiently.