Learning Journal - Week 32
During this past week in CST-334, we continued talking about thread locks and we explored condition variables, got introduced to semaphores and flagging, and dove into some common concurrency problems.
Recap
We begin by digging into condition variables. They aim to solve one of the biggest problems of "spinning", which is wasting thread efficiency. The naive approach for waiting on a thread to be finished is by simply creating a global flag variable (int done = 0 or = 1 for example), and performing a while loop until done is set to 1. The downside of this approach being that having the thread constantly check this is wasted time and checks, so it'd be much more efficient to simply notify the thread when it's ready to proceed. Condition variables are the solution: they can signal and notify waiting threads that a processed has completed and a thread has freed, or they can be used to initiate a wait on a thread.
We were also introduced to the Anderson/Dahlin method for creating multi-threaded programs:
We begin by normally designing our class, followed by adding a single lock. We then need to add code to acquire and release that lock, and introduce zero or more condition variables (as mentioned above). Then, utilizing these condition variables, we create signal/broadcast calls, and finally add wait calls within loops. This is a standardized and straightforward process to help implement safe multi-threaded programs.
Alongside condition variables, we also discussed semaphores, which take a different approach to synchronization. Instead of manually managing locks and condition variables, semaphores act as a collection of "tokens" that allows a desired amount of threads to operate at once. This works because semaphores are fundamentally initialized as integers, where the value represents the number of allowed tokens/threads at a time. We can either perform a signal (increment) or wait (decrement) on them to update their state. When a thread decrements the semaphore, it will block itself if the result is negative and wait until it becomes positive again.
A topic discussed in the reading were some common concurrency bugs, such as Non-deadlock bugs like atomicity-violation and Order-violation. Like the category implies, they don't cause deadlocks but they do cause other inconsistencies and unexpected behavior while multi-threading. They make up a majority of concurrency bugs. The other type are deadlock bugs, which usually stem when a logical error in programming potentially lead to threads depending on each other to release locks. This can lead to circular dependencies and mutual exclusion control.
Reflection
Somethings I'm still trying to fully wrap my head around are semaphores. I understand them on a fundamental level, but I don't have the hands-on experience with them yet to see how they work in specific scenarios. I do know, however, how to utilize them in theory (such as initializing them to 0 to act as standard locks), but I know I'll learn more with hands on experience.
Something I did understand well was condition variables, as it was a topic we've already been discussing closely up to this point with mutual exclusion. Working with locks does make more intuitive sense to me, also because serialization is a topic I still have fresh in my mind from working with databases.
The learning from these past two weeks especially have helped a lot with my understanding of our group project reading. We decided to look into the Barrelfish operating system which tackles a lot of concurrency optimization problems that systems like Windows and Linux didn't prepare for with increasing core counts. Learning how locks work at this level have helped me understand and appreciate on a deeper level what exactly Barrelfish is aiming to solve.
Comments
Post a Comment