Concurrent Counter with Thread Safety
This challenge focuses on implementing a thread-safe counter in Rust. Rust's ownership and borrowing system provides powerful tools for ensuring memory safety, but achieving thread safety requires careful consideration of shared mutable state. You'll be building a counter that can be safely incremented by multiple threads concurrently.
Problem Description
You are tasked with creating a Counter struct that provides a thread-safe mechanism for incrementing and retrieving the current count. The Counter should be initialized with an initial value. It must provide the following methods:
new(initial_value: i32) -> Counter: Creates a newCounterwith the given initial value.increment(): Increments the counter by 1. This method must be thread-safe.value() -> i32: Returns the current value of the counter. This method must be thread-safe.
The key requirement is that multiple threads can concurrently call increment() without causing data races or inconsistent state. Rust's concurrency primitives (like Mutex or RwLock) should be used to achieve this.
Expected Behavior:
- Multiple threads calling
increment()concurrently should eventually reflect in thevalue()method. - The
value()method should always return a consistent and accurate count, even when called concurrently withincrement(). - The program should not panic due to data races or other concurrency issues.
Edge Cases to Consider:
- High contention: Many threads attempting to increment the counter simultaneously.
- Rapid increment and value retrieval: Threads rapidly incrementing and then immediately retrieving the value.
- Initial value of zero.
- Large number of increments.
Examples
Example 1:
Input: Two threads, each calling `increment()` 1000 times.
Output: The final value of the counter should be 2000.
Explanation: Each thread increments the counter 1000 times, so the total increment is 2000.
Example 2:
Input: Five threads, each calling `increment()` 500 times, followed by a call to `value()`.
Output: The final value of the counter should be 2500. All calls to `value()` should return 2500 after all threads have finished.
Explanation: Each thread increments the counter 500 times, so the total increment is 2500.
Example 3: (Edge Case - High Contention)
Input: 100 threads, each calling `increment()` 100 times concurrently.
Output: The final value of the counter should be 10000.
Explanation: Demonstrates the thread safety under high contention.
Constraints
- The
Counterstruct must be thread-safe. - The
increment()method must be atomic with respect to otherincrement()calls. - The
value()method must return a consistent value even when called concurrently withincrement(). - The solution should be reasonably efficient. While performance is not the primary focus, avoid unnecessary overhead.
- The initial value can be any
i32value. - The number of threads incrementing the counter can be arbitrarily large (within reasonable system limits).
Notes
- Consider using Rust's standard library concurrency primitives like
MutexorRwLock. - Think about the order of operations when incrementing and retrieving the value to avoid race conditions.
- Testing with multiple threads is crucial to verify thread safety. Use the
std::threadmodule to create and manage threads. - Rust's borrow checker will help you catch many concurrency errors at compile time, but thorough testing is still essential.
- Focus on correctness first, then consider performance optimizations if needed.