🌍🚀 eMeetup Series Event 1: Thread Safety in Go 🌍🚀
It was fantastic fun kicking off the eSeries on Thursday 1st July with Preslav Mihaylov's talk on Thread Safety! If you missed out on this one then fear not as we'll be sending over a post event video so keep an eye out for that (or be notified as soon as its live by clicking to Register at the top of the page).
Here's a quick recap below of our Event 1 winners, Preslav's answers to the questions submitted before and during the event and what's coming up next...
🏆 Event 1 Category winners - Congrats guys!! 🏆
Domas Tamasauskas opted for some Go merch shipping the Gopher T shirt for best pre-event question (selected by Preslav)
Most Upvoted Question:
Go enthusiast Sagar Sonwane won the in-talk Q & A competition for the most upvoted question voted for by attendees and claiming the OPPO Enco M31 bluetooth earphones.
Ice Breaker Gopher Card Game:
Also, the highly accomplished Stefanos Chrs showed Golang is not the only string to his bow as he lead from the off and held off all competitors to win the ice breaker Gopher card race taking home some Amazon gift vouchers.
🧠 Q & A ---> Speaker Preslav's thoughts...🧠
Q) How to write tests to make sure that code is thread-safe or at least write enough tests to allow race detector to find data races?
'There are some techniques & caveats when writing tests for concurrent functions. In a nutshell, you want to trigger enough goroutines to trigger a thread-safety issue, avoid adding accidental synchronization (e.g. using a thread-safe RNG which by accident synchronizes your function as well) & have a way to verify the expected result in the end.
A good overview of how to do this is available in Chapter 12: Testing Concurrent Programs in the Java Concurrency in Practice book I recommended.'
Q) How to solve multiple readers single writer problem with Go's concurrency primitives when correctness is more important than performance? How about when performance matters more?
'Usually, you can use the sync.RWMutex which Go provides to lock the read paths using RLock and the write paths using Lock.
Just avoid the pitfall of not synchronizing the reads at all, as we discussed in the talk.
Now, there are also more high-performance alternatives at a cost of code which is harder to reason about. Using the atomic package provides you opportunities to achieve synchronization using optimistic concurrency rather than the traditional locking mechanism (pessimistic concurrency), but it leads to more complicated algorithms for non-trivial data structures (see non-blocking algorithms)'
Q) How to design a system so that data structures don't have to be thread-safe? Will it work when some of the fields are pointers?
'That ought to be a very ideal situation and is highly unlikely to be achieved in practice.
But if you're able to confine one data structure per goroutine, you need not synchronize them because they are only local for the goroutine.'
Q) Since go has the ability to utilize CPU cores for concurrency and we can define the number of cores we wish to use using the runtime library. So how do we decide what are the sufficient no of cores our application should use in order to get the best performance?
'You can leverage https://github.com/uber-go/automaxprocs'
Q) Being a novice go programmer I often find myself stuck in deciding how to structure my go application. So i wanted to ask, what kind of code structuring style you personally prefer and what kind of architecture does an organization like Uber use?
'I'd suggest checking out "Clean Architecture" - it covers the main principles which underpin most frameworks for structuring your app.
And if you're looking for a quick solution which is OK, structure your apps in controllers, services, repository, handlers & you'll be OK for small to medium size apps.'
Q) Should we always use `sync.Map` in favour of map + mutex for a thread-safe map? Are there any exceptions?
'If your data structure needs a single map, then I think sync.Map is a great option. If you need several data structures which need to be synchronized together, then using a vanilla mutex is often better code-wise although you might not reap the greatest performance benefits.'
Q) Does using synchronization primitives affect the performance, since we are constantly locking and unlocking. And can we use channels instead to achieve thread-safety?
'The main source of performance issues with locks is lock contention - too many threads waiting for a lock frequently. If a lock is not contended, it is not a big problem.
Using channels is not always an option - if you're starting your own goroutines, it is often best to leverage them instead of traditional means of concurrency. However, if you're using a framework which starts goroutines (e.g. http servers), you can't rely on channels. In those cases, you have to fallback to mutexes & etc.'
Thanks, Feedback and what's up next?
For the future schedule and details on how to sign up: https://www.gonaviro.com/blog/2021/05/emeet-up-golang-series
Huge thanks to Preslav for sharing his knowledge and everyone that attended; fantastic to see so much engagement and positivity at the event. Please do share any feedback on what you thought about the event, any improvements or parts of the eMeetup format that you really enjoyed and would want to remain. It would be valuable for us to hear from you and keep refining our series to deliver worthwhile events.
Next we have ex Spotify Go Engineer Andre Eriksson on Thursday 5th August talking on Go best practices and a new high-performance framework 'Encore' that’s been getting some exciting feedback on Github.. https://www.gonaviro.com/blog/2021/05/the-backend-framework-with-super-powers-thursday-5th-august