Synchronization Techniques
When working with Goroutines, it's important to ensure that access to shared resources is properly synchronized to avoid race conditions and other concurrency-related issues. Go provides several synchronization primitives that can be used to coordinate the execution of Goroutines.
Mutex
The sync.Mutex
type in Go is used to provide mutual exclusion, ensuring that only one Goroutine can access a shared resource at a time. Here's an example:
package main
import (
"fmt"
"sync"
)
func main() {
var count int
var mutex sync.Mutex
wg := sync.WaitGroup{}
wg.Add(100)
for i := 0; i < 100; i++ {
go func() {
defer wg.Done()
mutex.Lock()
defer mutex.Unlock()
count++
}()
}
wg.Wait()
fmt.Println("Final count:", count)
}
In this example, we use a sync.Mutex
to protect the count
variable from being accessed by multiple Goroutines simultaneously. The Lock()
and Unlock()
methods are used to acquire and release the lock, respectively.
WaitGroup
The sync.WaitGroup
type in Go is used to wait for a collection of Goroutines to finish. It's often used in conjunction with Goroutines to ensure that the main Goroutine waits for all the spawned Goroutines to complete. Here's an example:
package main
import (
"fmt"
"sync"
)
func main() {
wg := sync.WaitGroup{}
wg.Add(2)
go func() {
defer wg.Done()
fmt.Println("Goroutine 1 completed")
}()
go func() {
defer wg.Done()
fmt.Println("Goroutine 2 completed")
}()
wg.Wait()
fmt.Println("All Goroutines completed")
}
In this example, we use a sync.WaitGroup
to wait for two Goroutines to finish. The Add()
method is used to specify the number of Goroutines, and the Done()
method is called within each Goroutine to indicate that it has completed.
Channels
Channels in Go are a powerful synchronization mechanism that allow Goroutines to communicate with each other. Channels can be used to pass data between Goroutines and to coordinate their execution. Here's an example:
package main
import "fmt"
func main() {
ch := make(chan int)
go func() {
ch <- 42
}()
value := <-ch
fmt.Println("Received value:", value)
}
In this example, we create a channel of type int
and use it to pass a value from one Goroutine to the main Goroutine. The <-
operator is used to send and receive values on the channel.
By using these synchronization techniques, you can effectively coordinate the execution of Goroutines and ensure that your Go applications are thread-safe and scalable.