Understanding Pessimistic Locking with Mutex

Watch the video explanation ➔

Write an engineering article with a concrete timeline in Markdown format from the following transcript.

so we all know the standard count plus plus operation is not Atomic and in the previous video we went into the semi language code to understand why exactly it is it do watch it if you haven't already I'll link it in the description Down Below in this video we will make count plus plus Atomic using pessimistic login it is one of the most popular ways of writing correct multi-threaded programs in the process we will also understand what pessimistic locking is the core intuition behind mutexes and how to use them so here I am using a cloud-based IDE called replica to code this out because it is cloud-based I do not have to set up anything locally and I just need a browser to get started replit has an amazing llm assistant named Ghostwriter which is extremely powerful because it is vertically trained on programming and software development knowledge base and the best part is it spits out answers and suggestions in the context of the code that you are writing apart from this replit has something called as bounties which can help you make some money on the site as a bounty companies post paid projects on web applications AI tools Discord Bots and so much more you can pick the one that interests you complete it and get paid just sign up on replied with my link in the description down below once you sign up on the home page itself you can see a section called bounties when you click on explode you can see the projects that are listed there and the amount of money you can make out of that pick the one that interests you apply for it and just get started so the link for the sign up is in the description down below go through that sign up for my link and this to be honest is a great way to make some money on the site doing what you love so do check that out so now let's head back to the video so when two threads try to update the same variable the final value may be inconsistent or incorrect to see this in action we would run a simple we would write a simple golang based code in which we'll have a global variable called count and we would run 1 million threads trying to update the same variable count to plus plus on it at the end of it we should expect the final value to be 1 million but would we get it we would definitely not we would see that the value becomes Incorrect and then we would wrap it with pessimistic locking to make it 1 million every single time so let's jump right into the code and see things in action to do this what we would do is I'll Define a variable called count int equal to zero and then I would say count equal to 0 I'll write a function called do count in which what this function would do is it would spin up 100 000 threads so for sorry 1 million threat for I equal to 0 I is less than one one two three four five six one million I plus plus and I'll write another function called increment count whose job would be to increment the count variable and make it plus plus so I'll do count plus plus count plus plus and done okay now this is what we are doing up until now but what we would want to do is would want to update discount plus plus in one shot so by using multiple threads so I'll just prefix it with go in go like if you prefix a function call with go it would spin it off in its go routine and what we would want we would want our main function to wait until all go routines are done executing so for that we would Define a WG sync dot Wade group ideally what like what it helps us achieve is whenever we spin off a go routine if we do WG dot add one and one my go routine is done executing with do WG dot done it would just increment that value by the number that you passed over here in an atomic fashion and done will reduce it by one right now the key thing is that when you do WG dot weight this is a blocking call it would wait until my wg's value is zero when would a WG value become 0 when all of my threads because for every single thread we are doing plus one and we are doing minus one when it is done executing so it would become 0 when all of my threads are done executing right and once it is executed then I will do fft Dot println and add print the value of count right and now if I run the code we should see some numbers that the final value of count and we see that the final value is not equal to 1 million it should be 1 million but it is not 1 million we see different types of values Let's see 9988k we are seeing now 993k right so the value is not 1 million which is what we expect this shows that the count plus plus is not Atomic in nature right so now what do we do we want to make it uh correct right so again as I always say it's easy to write multi-threaded program but difficult to write correct multi-threaded program so let's understand why is this happening right so here the root cause of the problem is the operation count plus plus because that is not Atomic and why it is not at all like because it is not that I mean we can't go and change the implementation of count plus plus right so what do we do we would want this part of code to be executed by just one thread at a time which means no two threads should be executing count plus plus at the same time so what do we do we wrap it with locks this technique is pessimistic locking we are being pessimistic about the thing and we would want the thread to acquire the lock and then do count plus plus and then release the log this way if I have n threads waiting to acquire the lock one of them would succeed which would execute count plus plus and then it would release a log and then only the next thread would come in otherwise it would not right so this is really important because count plus plus is not Atomic we are wrapping it with pessimistic law this implementation is classically called New ticks basically mutually exclusive implementation right so what we are doing is we are making this critical section to be mutually exclusive for one thread or one go routine at a time right which is what we would Implement now so in the function that we wrote what we would want is ideally we would want discount plus plus to be executed by just one thread at a time so what do we do we Define a mutex let's say let's call it mu sync dot mutex almost every language has some or the other mutex implementation you can pick your favorite and go about it and what we would do is we would just wrap this mutex with mu DOT log and mu dot unlock that's it and now when I do it now when I run the code I would always see one million no matter how many times I run you can see 1 million every time it is getting output it's always output 1 million no matter what why is it happening because we just wrapped our count plus plus with mu Dot Lock and mu dot unlock what are what is it doing it's ensuring that even if I have 1 million threads all wanting to do count plus plus it would only one thread would be able to acquire the lock move forward and then release the log once it releases a lot another thread would move in do plus plus and then release the log that's the core idea behind it but now you can very clearly see the problem in this approach the problem in this approach is performance that you had one million threads all wanting to do this but 999 000 threads are just waiting for one thread to complete so whenever you apply pessimistic locking one of the key things for you to understand is that your time your performance will degrade because a lot of things are just waiting for like a lot of threads which could execute are just waiting for some other thread to release the lock right so always remember the specific locking is not free of cost and the amount of code you put between your mutex in this case it's just one line the amount of code you put between your mutex needs to be bare minimum so that you can maximize the parallelism ideally what you could have done you could have just wrapped this entire in count function in a mutex but why like imagine this function being big and having a lot of other statements you are just unnecessarily waiting for things that you could proceed like some threads could proceed on that but you are just making them wait unnecessarily so a golden rule to write performant multi-thread program is keep your size of the critical section to a bare minimum do not bloat up your critical section put the logs wherever necessary not at not just wrap entire function together because it would just degrade your performance by a massive margin right but your program will have to be like your program needs to be correct in this case the correctness was that the final value of count should be equal to 1 million which is what we achieved so again as I said writing concurrent program is easy writing correct concurrent program is difficult and it's really tough to write performant correct multi-threaded programs so yeah this is all what I wanted to cover in this one I hope you found a concept of SMS like now I hope you understand the concept of pessimistic locking really well so that you can apply it at hundreds of places depending on the language that you are using be it C C plus plus Java every language has some of the other constructs that comes out of the box for you to use to have this guard rails if I may put it like mutexes Simba first these are your friends to play around with multi-threaded program in Java you have synchronized block that does something very similar right so yeah this is all what I wanted to cover in this one I hope you found it interesting hope you found it amazing that's it for this one I'll see in the next one thanks thank you [Music]

Here's the video ⤵

Courses I teach

Alongside my daily work, I also teach some highly practical courses, with a no-fluff no-nonsense approach, that are designed to spark engineering curiosity and help you ace your career.


System Design Masterclass

A no-fluff masterclass that helps SDE-2, SDE-3, and above form the right intuition to design and implement highly scalable, fault-tolerant, extensible, and available systems.


Details →

System Design for Beginners

An in-depth and self-paced course for absolute beginners to become great at designing and implementing scalable, available, and extensible systems.


Details →

Redis Internals

A self-paced and hands-on course covering Redis internals - data structures, algorithms, and some core features by re-implementing them in Go.


Details →


Writings and Learnings

Knowledge Base

Bookshelf

Papershelf


Arpit's Newsletter read by 100,000 engineers

Weekly essays on real-world system design, distributed systems, or a deep dive into some super-clever algorithm.