Demystifying Go (Golang), Google’s Programming Language: Sync Package - RWMutex and Once
Vagner Nascimento
Software Engineer | Go (golang) | NodeJS (Javascrit) | AWS | Azure | CI/CD | Git | Devops | Terraform | IaC | Microservices | Solutions Architect
Introduction
Welcome back to the world of the sync package! If you’ve already ventured into using Mutex, get ready to discover the next level of concurrency programming: the powerful RWMutex and the elegant Once. The RWMutex offers refined mutual exclusion, giving your application that extra performance boost, while Once is a sleek way to ensure something happens only once — yes, perfect for a controlled singleton. Let’s dive in, Gopher! An ocean of knowledge awaits.
RWMutex
The RWMutex is like the smarter, stronger sibling of Mutex. Not only does it offer Lock and Unlock functions like Mutex, but it also provides an option to lock for read-only operations, reducing deadlock risks and optimizing locks. With the RLock and RUnlock methods, you can lock a resource for reading only, enabling more efficient access for resources that don’t require writing. Pretty cool, right?
In the example below, we simulate a bank account with a balance, a function for making withdrawals (Debit), and another for checking the balance (GetBalance). In the main function, we create an account with an initial balance of 5000, launch a goroutine to print the balance every millisecond, and then fire up multiple goroutines to debit $1 until the balance reaches zero. Everything looks perfect, right? Well, not so fast.
This code is a prime target for concurrency issues. If we run it with the --race flag, we’ll see that we have a race condition, and it’s only a matter of time before inconsistencies pop up. Check the test in the image below.
To fix this, we’ll add a new attribute called rwm of type RWMutex to the BankAccount structure. In the Debit function, we’ll use Lock and Unlock on rwm because we’re both reading and modifying the balance. In GetBalance, we’ll use RLock and RUnlock, blocking only for reading, leaving it open for writing, and boosting performance overall. Here’s the updated code:
And, of course, we’ll run the test again with the --race flag to confirm that the problem is solved. Check out the results in the image below.
领英推荐
Congratulations, Gopher! Another race condition resolved elegantly.
Once
The sync package also brings us the Once structure, which is the key to executing a function only once, even if multiple goroutines try to call it simultaneously. It’s perfect for scenarios where you need to ensure that something only happens a single time, like singleton initialization. Remember the Connector example from the previous post? Let’s revisit it for a quick refresher.
This code creates two connectors with different attempts (attempt 1 and attempt 5) because checking for nil on conn alone doesn’t work. To fix this, we’ll add a new once variable of type sync.Once right below conn and use the Do function to initialize the connector. Now, regardless of the number of calls, the connector will be created only once. See the updated code:
And with the --race test enabled, you’ll see the code is free from race conditions. Check the image below.
Conclusion
These sync package structures — RWMutex and Once — are essential for building safe and performant Go applications. RWMutex provides refined control over read and write locks, improving efficiency and reducing deadlock risks. Once, on the other hand, is perfect for safe, one-time initializations, ideal for preventing resource duplication. With these, you’re much better equipped to navigate the challenges of concurrent programming.
Keep exploring and learning, Gopher! After all, the road of programming is long, but with Go, it’s a bit more fun and efficient.
Senior Software Engineer | Golang Developer | Node.js | React |Microservices | DevOps
2 个月Another amazing article on using concurrency properly! Great content!
Lead Fullstack Engineer | Typescript Software Engineer | Nestjs | Nodejs | Reactjs | AWS | Rust
3 个月Awesome
FullStack Developer @ Itaú Digital Assets | Go | TS | Blockchain | Aws
3 个月Great advice
Senior Software Engineer | Java | Spring | AWS
3 个月I'll keep this in mind
Data Engineer | Analytics Engineer | Python SQL AWS Databricks Snowflake
3 个月Very helpful!