KeyLockedMemoryCache 1.0.1
dotnet add package KeyLockedMemoryCache --version 1.0.1
NuGet\Install-Package KeyLockedMemoryCache -Version 1.0.1
<PackageReference Include="KeyLockedMemoryCache" Version="1.0.1" />
<PackageVersion Include="KeyLockedMemoryCache" Version="1.0.1" />
<PackageReference Include="KeyLockedMemoryCache" />
paket add KeyLockedMemoryCache --version 1.0.1
#r "nuget: KeyLockedMemoryCache, 1.0.1"
#:package KeyLockedMemoryCache@1.0.1
#addin nuget:?package=KeyLockedMemoryCache&version=1.0.1
#tool nuget:?package=KeyLockedMemoryCache&version=1.0.1
KeyLockedMemoryCache
KeyLockedMemoryCache
A high‑performance, per‑key locking cache built on top of .NET MemoryCache.
Designed for extreme concurrency, zero duplicated loads, and maximum speed.
🚀 Why KeyLockedMemoryCache?
MemoryCache is thread‑safe, but it does not prevent multiple threads from loading the same value at the same time.
This package solves that problem with a per‑key locking mechanism that guarantees:
- Only one thread loads the value
- All other threads wait and reuse the result
- No duplicated work
- No race conditions
- No global locks
- No performance bottlenecks
Perfect for:
- High‑concurrency APIs
- Microservices
- Database‑heavy workloads
- Expensive I/O operations
- Distributed systems with local caching
✨ Key Features
🔒 Per‑key locking
Prevents duplicated loads under heavy concurrency.⚡ Ultra‑fast access path
Cache hits avoid locking and return immediately.🧹 Automatic lock cleanup
Locks are removed when the cache entry expires.🧠 MemoryCache‑powered
Leverages .NET's built‑in cache with expiration, eviction, and memory pressure handling.🧩 Zero dependencies
Works everywhere: .NET Framework, .NET Standard, .NET Core, .NET 5/6/7/8.🧵 Thread‑safe by design
UsesConcurrentDictionaryand fine‑grained locking.🛠️ Simple API
Just callGetOrAdd(prefix, key, loader)and you're done.
📦 Installation
powershell:
-Install-Package KeyLockedMemoryCache
or
-dotnet add package KeyLockedMemoryCache
🧰 Usage Example (simple)
var cache = new InternalCache<string>(30); // 30 minutes expiration
string value = cache.GetOrAdd("User", userId, () ⇒ { // This code will run only once, even under heavy concurrency return LoadUserFromDatabase(userId); });
🏎️ High Concurrency Example
var cache = new InternalCache<Product>(10);
Parallel.For(0, 200, _ ⇒ { var result = cache.GetOrAdd("Product", 42, () ⇒ { // Expensive operation executed only once return LoadProductFromApi(42); }); });
##🧪 Performance Characteristics This cache is optimized for extreme read concurrency:
- Cache hits: no locking, O(1) lookup
- Cache misses: only one loader execution
- Lock contention: limited to a single key
- No global locks
- Minimal allocations (optimized key building, static dictionaries, no closures)
- Expiration handled by MemoryCache (no background threads) Benchmark Example
- 200 threads
- 500 iterations each
- 100,000 total accesses
- Loader executed: 1 time
- Total time: 10–15 ms (depending on hardware)
##🧱 How it works (internals)
- Build a fast key using string.Concat(prefix, ":", keyValue)
- Try to get the value from MemoryCache (fast path)
- If missing, obtain a lock object from a static ConcurrentDictionary
- Enter a per‑key lock
- Double‑check the cache (avoids race conditions)
- Execute the loader only once
- Store the value with a CacheItemPolicy
- When the entry expires, the lock is automatically removed
##🔐 Thread Safety Guarantees
- No two threads will execute the loader for the same key
- No global lock is ever used
- No deadlocks (locks are per‑key and short‑lived)
- No memory leaks (locks are removed on expiration)
- Safe for high‑concurrency scenarios (APIs, microservices, background jobs)
##🧹 Removing a value cache.Remove("User", userId);
This removes:
- The cache entry
- The associated lock
##🧩 API Summary | | | | GetOrAdd(prefix, key, loader) | | | Remove(prefix, key) | |
##📄 License MIT License.
##❤️ Contribute Issues and pull requests are welcome. If you find this useful, consider starring the repository
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- System.Runtime.Caching (>= 10.0.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.