Using Mutex in GO on a real world project.

GOMutexConcurrency

What will I learn?

I will be able to understand what is a Mutex, and how it works in concurrent processes. I will apply a mutex on a real-world project to see how we can benefit from it.

What is a Mutex?

Mutex is a library which works on locking methods to not be accessible from other concurrent processes, until the mutex says that it's open. Think of a mutex as the traffic police, and when the police blocks a road thats a Lock(), no car will pass until the Police opens Unlock() the road. !

How does it work?

As I would read in the Go docs a Mutex must not be copied, if I call the Lock() method then the mutex is locked and any other lock call will be waiting for it to be Unlock()-ed. If I call the Unlock() method then the mutex will unblock itself and tell the lock method that Mutex is open. Every time I copy a mutex then the state is unlocked, meaning it will have no effect.

Before I describe how to apply on a project, try to set up this project in your maching, and see how it works.

How do I apply it on a project?

This is a project that I was working on, it's about assigning gifs to employees, and each employee has to have one gift, first come first serve. I have employees and gifts in two json files and they look like this:

//gifts json
[{
    "name": "netflix card",
    "categories": ["music", "film", "pets", "scifi"]
    }, {
        "name": "gold plated water bottle",
        "categories": ["power lifting", "triathlons", "football", "crossfit", "handball", "running"]
    }
]

//employees json
[{
    "id": 1,
    "name": "severin",
    "interests": ["high end audio", "photography", "sitting comfortably"]
    }, {
        "id": 2,
        "name": "oliver",
        "interests": ["pets", "scifi", "music making"]
    }
]

As beautiful as this looks I have to move on.

Next you will see the project structure in github. The main.go file has the following content

///...
var mutex = sync.Mutex{} //here we initialise Mutex, keep in mind this is outside of the methods.

func handler(w http.ResponseWriter, r *http.Request) {
    employeeId, err := strconv.Atoi(r.URL.Path[1:])
    if err != nil {
        fmt.Fprintf(w, "Employee with id %s not found", r.URL.Path[1:])
        return
    }
    mediator := application.CommandMediator{Mu: &mutex} //here we pass the mutex by reference
    mediator.Add(command.NewAssignGiftToEmployeeCommand(employeeId))

    mediator.Run()
}

func main() {
    http.HandleFunc("/", handler)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

If I go ahead and open CommandMediator I will see that I use the mutex there to Lock() and defer Unlock(), and defer is like saying execute this when you are done.

type CommandMediator struct {
    Mu       *sync.Mutex // important
    commands []Command
}
///...
func (cm *CommandMediator) Run() {
    cm.Mu.Lock() //locking
    defer cm.Mu.Unlock() //unlocking when job is done

    for _, command := range cm.commands {
        command.Execute() // executing the commands
    }
}

As I can see in the method Run() above, I am calling the mutex that is being passed from main file, and then locking it. So that means that the mutex that I am getting it's the same mutex that every one else is getting when doing the request.

So that makes sense, since we are pasing mutex by reference and other people have the same instance of it, someone might be faster than me in locking it then I must wait for the Unlock().

How long do I have to wait for my request?

Turns out it all depends on how your project is organised, in my small project I have blocked the whole command, meaning no one can execute the command until you are done with it.

To make the change more obvious I have added a time.Sleep(5 * time.Second) which will sleep for 5 seconds, this is for me to try to simulate two request at the same time. So every time I run the command it will be blocked for 5 seconds.

How do I see the effect?

Since I have the project in my machine, I can open two terminal windows and execute this command in both of them, curl http://localhost:8080/1 this will try to assign a gift to employee 1, you will see that one of the responses will wait until the other one is done.


If you enjoyed this, please give a clap or share with your network!

Suggested Posts

Navigating Deadlock Challenges in Real-World Scenarios

New

Read more →

CQRS explained with examples

Read more →

Serverless architecture

Read more →