Skip to main content

Command Palette

Search for a command to run...

What is a Mutex in Golang?

Updated
3 min read
What is a Mutex in Golang?
K
On kevincoder.co.za, I write about my journey as a developer working across Django, Go, and everything in between, from large-scale systems to small, useful tools. Programming has been my passion for over 15 years now. I love learning new skills and am thrilled I can share these with you! A big part of my career was built on knowledge shared by others; in the main, open-source projects, forums, and communities like Stack Overflow. This blog is my way of contributing back and sharing what I’ve learned along the way.

When you build Golang programs, you will almost always find a use for Goroutines.

Goroutines are powerful and generally easy to use, however, if you need to modify some piece of data that is shared between Goroutines - then you may run into some trouble with data integrity.

In this article, we will look at what is a "Mutex" and how to use it.

What is a Mutex?

In Golang; a Goroutine is essentially a function that is put in a background queue and executed concurrently whenever resources are available.

package main

import (
    "fmt"
    "sync"
)

var NUM_PROCESSED = 0

func countNumProcessed(wg *sync.WaitGroup) {
    defer wg.Done()
    NUM_PROCESSED++
}

func main() {
    var wg sync.WaitGroup

    for i := 0; i < 500; i++ {
        wg.Add(1)
        go countNumProcessed(&wg)
    }

    wg.Wait()
    fmt.Println(NUM_PROCESSED)
}

If we remove the "go" keyword, each function call to "countNumProcessed" will block the loop and wait for this function to finish before continuing with the loop.

When you use the "go" keyword, these functions will be run concurrently. Which then makes it possible for 2 or more functions to modify the variable at the same time.

If you run this code a few times - you will notice that the total count fluctuates.

This is because each Goroutine, for a few nanoseconds copies the value of "NUM_PROCESSED" in memory to increment it and then update the variable.

If two (or more) Goroutines copy the value around the same time, they will not be aware of updates made by other Goroutines.

For example: let's assume the value was "200"; each of the two Goroutines will add "1" i.e. "201". Thus the value for "NUM_PROCESSED" will become "201" instead of "202".

This is where a Mutex comes in handy. A Mutex will create a "lock" in the process - so that only one Goroutine can update "NUM_PROCESSED" at a time.

Goroutines

The other Goroutines will then be paused until the lock is released. This is very similar to a queue - when each Goroutine releases the lock, the next one acquires a new lock and the process continues until all the queued Goroutines finish updating "NUM_PROCESSED".

A Mutex example

We can modify the above code to introduce a Mutex as follows:

package main

import (
    "fmt"
    "sync"
)

var NUM_PROCESSED = 0
var MUTEX sync.Mutex

func countNumProcessed(wg *sync.WaitGroup) {
    defer wg.Done()
    MUTEX.Lock()
    NUM_PROCESSED++
    MUTEX.Unlock()
}

func main() {
    var wg sync.WaitGroup

    for i := 0; i < 500; i++ {
        wg.Add(1)
        go countNumProcessed(&wg)
    }

    wg.Wait()
    fmt.Println(NUM_PROCESSED)
}

You will notice with the above code, that no matter how many times you run this function, it will always print "500". This is precisely the same amount of goroutines created in the for loop.

More from this blog

Kevin Coder | tutorials, thought experiments & tech ramblings

37 posts