Concurrent Counter: A Python Threading Challenge
This challenge focuses on understanding and implementing basic threading in Python. You'll be tasked with creating a program that uses multiple threads to increment a shared counter, demonstrating how threads can operate concurrently and the potential pitfalls of shared resources. This is a fundamental exercise in concurrent programming and crucial for understanding multi-core processing.
Problem Description
You need to write a Python program that utilizes multiple threads to increment a shared counter variable. The program should create a specified number of threads, each of which will increment the counter a specified number of times. After all threads have completed, the program should print the final value of the counter.
Key Requirements:
- Thread Creation: Create a function that represents the work each thread will perform (incrementing the counter).
- Shared Resource: Use a shared variable (the counter) that all threads can access.
- Concurrency: Ensure that multiple threads are running concurrently to increment the counter.
- Synchronization (Important!): Implement a mechanism to prevent race conditions when multiple threads try to update the counter simultaneously. A
Lockis the recommended approach. - Final Value: After all threads have finished, print the final value of the counter.
Expected Behavior:
The final value of the counter should be equal to the number of threads multiplied by the number of increments per thread. Without proper synchronization, the final value will likely be incorrect due to race conditions.
Edge Cases to Consider:
- A large number of threads and increments can exacerbate race conditions if synchronization is not implemented correctly.
- The number of threads and increments should be positive integers. Handle invalid inputs gracefully (though the challenge primarily focuses on threading, basic input validation is good practice).
Examples
Example 1:
Input: num_threads = 2, increments_per_thread = 1000
Output: 2000
Explanation: Two threads each increment the counter 1000 times. With proper synchronization, the final value will be 2000.
Example 2:
Input: num_threads = 5, increments_per_thread = 500
Output: 2500
Explanation: Five threads each increment the counter 500 times. The final value should be 2500.
Example 3: (Edge Case - Large Numbers)
Input: num_threads = 10, increments_per_thread = 10000
Output: 100000
Explanation: Ten threads each increment the counter 10000 times. This tests the robustness of your synchronization mechanism under higher load.
Constraints
num_threadsmust be a positive integer between 1 and 100 (inclusive).increments_per_threadmust be a positive integer between 1 and 10000 (inclusive).- The program should complete within 5 seconds. (This is a soft constraint; the primary focus is on correctness, but performance matters in real-world threading scenarios.)
- Use the
threadingmodule in Python.
Notes
- Race conditions occur when multiple threads access and modify shared data concurrently, leading to unpredictable results.
- A
Lockis a synchronization primitive that allows only one thread to access a shared resource at a time, preventing race conditions. Uselock.acquire()before accessing the shared counter andlock.release()after. - Consider using a
whileloop within the thread function to ensure all increments are performed before the thread exits. - Think carefully about where to acquire and release the lock to minimize contention and maximize performance. Acquiring the lock for the entire thread's execution is generally not optimal.