Let us explore some features provided in this article.
Golang Mutex
The sync.Mutex provides a mutex primitive, which allows for mutual exclusions for shared resources preventing race conditions.
Example usage:
import "sync"
func main() {
var i = 10
mutex := &sync.Mutex{}
mutex.Lock()
// only one goroutine can access this code at a time
i++
mutex.Unlock()
}
Read/Write Mutex
The sync.RWMutex provides a reader/writer mutex. It offers similar methods as a primitive mutex but can allow concurrent reads using RLock and RUnlock methods:
mutex := &sync.RWMutex{}
mutex.Lock()
// only one goroutine can access this code at a time
i++
mutex.Unlock()
mutex.RLock()
i++ // concurrent reads
mutex.RUnlock()
The previous example can allow over one goroutine to read the code. Unlike sync.Mutex, which allows exactly one reader and one writer at a time.
Waitgroups
The sync.Waitgroup is used to offer a blocking mechanism for goroutines. Using a Waitgroup, you can block the execution of a function until all the goroutines have finished executing.
It works by creating a counter which holds the number of goroutines to wait. Once a goroutine is complete, the counter decreases by 1. Once the counter is 0, the Waitgroup unblocks the execution.
To add a value to the Waitgroup counter, we can use the Add() method, which takes an integer value.
To remove a goroutine from the counter after completion, we use the Done() method. For example:
import (
"fmt"
"sync"
)
func main() {
wg := &sync.WaitGroup{}
for i := 0; i < 5; i++ {
wg.Add(1)
go func(x int) {
fmt.Printf("Worker: %d running\n", x)
wg.Done()
}(i)
}
wg.Wait()
}
In the previous example, we increment the value of the Waitgroup counter by 1 through the use of the Add() function.
Once a goroutine is complete, we decrement the counter by 1 using the Done() method. The previous code should return output as:
Worker: 2 running
Worker: 1 running
Worker: 3 running
Worker: 0 running
Once
The sync.Once primitive ensures that a function runs only once. An example is as shown below:
RunOnce := func() {
fmt.Println("Run once")
}
done := make(chan string)
for i := 0; i < 5; i++ {
go func() {
once.Do(RunOnce)
done <- "Hi"
}()
}
for i := 0; i < 5; i++ {
<-done
}
Conclusion
This guide covers some basic and fundamental primitives provided by the Go sync package. In addition, synchronization primitives, such as mutex, Waitgroup, Pool, Read and Write mutex, and condition variables, were discussed. We hope you found this article helpful. Check out other Linux Hint articles for more tips and tutorials.