How to implement a generic thread-safe cache class in C# that stores key-value pairs.

clock icon

asked 3 months ago Asked

message

1 Answers

eye

42 Views

the goal is to create a class that allows you to store, retrieve, and remove key-value pairs in a thread-safe manner. Additionally, the cache should include an expiration mechanism so that items are automatically removed after a certain period.

1 Answers

To implement a generic thread-safe cache class in C#, the goal is to create a class that allows you to store, retrieve, and remove key-value pairs in a thread-safe manner. Additionally, the cache should include an expiration mechanism so that items are automatically removed after a certain period.

Here's a step-by-step guide on how to accomplish this:

1. Understanding the Requirements

  • Generic Class: The cache should be able to work with any type of key (TKey) and value (TValue). Hence, we need a generic class.
  • Thread-Safe: The class must handle concurrent access by multiple threads without causing data corruption or unexpected behavior.
  • Expiration Mechanism: Items in the cache should have an expiration time after which they are automatically considered invalid and should not be returned from the cache.

2. Choosing the Right Data Structure

  • Thread-Safety: C# provides the ConcurrentDictionary<TKey, TValue> class in the System.Collections.Concurrent namespace, which is ideal for this purpose. It is designed to handle concurrent operations and provides thread-safe methods for adding, retrieving, and removing items.
  • CacheItem: Since we need to store both the value and the expiration time for each entry, we should define a CacheItem class that contains these two pieces of information.

3. Designing the CacheItem Class

  • The CacheItem class will have two properties:

    1. Value: The cached value of type TValue.
    2. ExpiryTime: A DateTime indicating when the item should expire.
  • The class will also have a method IsExpired() to check if the current time is past the expiry time.

4. Implementing the Cache Class

  • Data Storage: Use a ConcurrentDictionary<TKey, CacheItem> to store the cache items.
  • Add Method: The Add method should accept a key, a value, and an expiration duration. It will create a CacheItem and store it in the dictionary.
  • Retrieve Method: The TryGetValue method will retrieve the item if it exists and is not expired. If the item is expired, it should be removed from the cache.
  • Remove Method: The Remove method will allow manual removal of items from the cache.
  • Clear Method (Optional): A method to clear all items from the cache.

5. Handling Expiration

  • Every time an item is accessed (via TryGetValue), its expiration status is checked. If the item has expired, it is removed from the cache, and the method returns false, indicating the item is no longer valid.

6. Putting It All Together: The Code

Here’s how the code would look:

csharp
using System; using System.Collections.Concurrent; public class ThreadSafeCache<TKey, TValue> { // The CacheItem class holds the value and its expiration time private class CacheItem { public TValue Value { get; } public DateTime ExpiryTime { get; } public CacheItem(TValue value, TimeSpan duration) { Value = value; ExpiryTime = DateTime.UtcNow.Add(duration); } public bool IsExpired() => DateTime.UtcNow > ExpiryTime; } // ConcurrentDictionary to hold the cache items private readonly ConcurrentDictionary<TKey, CacheItem> _cache = new ConcurrentDictionary<TKey, CacheItem>(); // Method to add an item to the cache public void Add(TKey key, TValue value, TimeSpan duration) { var cacheItem = new CacheItem(value, duration); _cache[key] = cacheItem; // Adds or updates the value in the cache } // Method to retrieve an item from the cache public bool TryGetValue(TKey key, out TValue value) { if (_cache.TryGetValue(key, out CacheItem cacheItem)) { if (!cacheItem.IsExpired()) { value = cacheItem.Value; return true; } else { // If the item is expired, remove it from the cache _cache.TryRemove(key, out _); } } value = default; return false; } // Method to remove an item from the cache public bool Remove(TKey key) { return _cache.TryRemove(key, out _); } // Optional: Method to clear the cache public void Clear() { _cache.Clear(); } }

7. Key Points in the Implementation

  • Thread-Safety: The ConcurrentDictionary handles all thread-safety concerns for adding, retrieving, and removing items. There’s no need for explicit locks or synchronization primitives, which simplifies the implementation.
  • Expiration Check: The expiration logic is handled within the TryGetValue method. If an item is expired, it’s removed from the cache immediately, ensuring that the cache only returns valid items.
  • Flexible Expiration: The cache allows you to specify different expiration durations for different items, offering flexibility in how long each item should remain valid.

You must sign in to submit an answer or vote.

Top Questions