Implementing Atomic Operations in Python with Locking
Atomic operations are fundamental in concurrent programming, ensuring that a sequence of operations appears to execute as a single, indivisible unit. This prevents race conditions and data corruption when multiple threads or processes access shared resources. This challenge asks you to simulate atomic operations in Python using locking mechanisms, as Python doesn't natively provide true atomic operations at the hardware level.
Problem Description
You are tasked with creating a class AtomicCounter that simulates an atomic integer counter. This class should provide the following methods:
increment(self, value=1): Atomically increments the counter by the givenvalue(defaulting to 1).decrement(self, value=1): Atomically decrements the counter by the givenvalue(defaulting to 1).get_value(self): Returns the current value of the counter.
The atomicity of these operations must be guaranteed using a threading.Lock. This means that only one thread can modify the counter's value at any given time, preventing race conditions. The threading.Lock should be initialized within the AtomicCounter class.
Examples
Example 1:
Input:
Thread 1: increment(5)
Thread 2: increment(3)
Thread 3: get_value()
Output: 8
Explanation: Thread 1 increments the counter by 5, Thread 2 increments it by 3. The final value is 5 + 3 = 8. The locking mechanism ensures these increments happen sequentially and without interference.
Example 2:
Input:
Thread 1: decrement(2)
Thread 2: increment(1)
Thread 3: get_value()
Output: -1
Explanation: Thread 1 decrements the counter by 2, Thread 2 increments it by 1. Assuming the counter starts at 0, the final value is 0 - 2 + 1 = -1.
Example 3: (Edge Case - Concurrent Decrement and Increment)
Input:
Thread 1: decrement(5)
Thread 2: increment(5)
Thread 3: get_value()
Output: 0
Explanation: If the counter starts at 0, Thread 1 attempts to decrement by 5, and Thread 2 attempts to increment by 5. Due to the lock, these operations will happen sequentially. The final value will be 0 - 5 + 5 = 0.
Constraints
- The initial value of the counter should be 0.
- The
valueparameter forincrementanddecrementshould be an integer. - The
incrementanddecrementmethods should handle negative values correctly. - The code must use
threading.Lockto ensure atomicity. - The solution should be thread-safe.
Notes
- Consider the order of acquiring and releasing the lock within each method.
- Think about how to handle potential exceptions within the critical section (the code protected by the lock). While not explicitly required, robust error handling is good practice.
- The focus is on demonstrating the concept of atomic operations using locks, not on optimizing for extreme performance. Readability and correctness are prioritized.
- You do not need to create actual threads for testing; simply call the methods sequentially to verify the logic. However, the design must be thread-safe.