A lock, or mutex, is a synchronization technique and important concurrent programming concept.
Suppose you are writing a Go program where there is more than one thread or goroutine executing concurrently. These goroutines share a variable among them – so you’ll want to ensure that only one goroutine accesses the variable at a time to avoid conflicts. This is where mutexes come in.
Mutex provides mutual exclusion to a resource, which means that only one goroutine can hold it at a time. Any goroutine must first acquire the mutex and then access the resource. If a goroutine tries to acquire the mutex already held by another goroutine, it will be blocked and will wait for the mutex to be released.
Alright, that’s enough talk about what mutexes are and how they work. Let’s see how they are implemented in Go.
Go’s standard library provides mutual exclusion with sync.Mutex
and its two methods: Lock
and Unlock
.
m.Lock()
: Locks the mutex, m
. If the lock is already in use, call goroutine blocks until the mutex is available.m.Unlock()
: Unlocks the mutex, m
. A run-time error is thrown if m
is not already locked.In this example, we declare the variable balance
, which will be our shared resource. We then define the counter()
function, which accesses and increments balance
using a loop.
We execute counter()
in two goroutines. Since both goroutines will access balance
at the same time, there will be conflicts. Hence, we wrap the loop inside the m.Lock()
and m.Unlock
so that only one goroutine can access balance at one time. time.Sleep()
is just there so goroutines can finish before printing balance
.
Try commenting out the mutex code – you will notice that balance is printed incorrectly.
package mainimport ("fmt""sync""time")func main() {// Balance: shared variablebalance := 0// A single mutex to be used by both goroutinesvar m sync.Mutex// Function that accesses balance multiple timescounter := func() {m.Lock()for i := 0; i < 10000000; i++ {balance = balance + 10}m.Unlock()}// Starting two goroutinesgo counter()go counter()// Waiting and then printing balancetime.Sleep(time.Second)fmt.Printf("Balance: %v", balance) // should be 200000000}