### KeyedSemaphoresCollection Usage Example in C# Source: https://github.com/amoerie/keyed-semaphores/blob/main/README.MD Demonstrates the creation and usage of two KeyedSemaphoresCollection instances, showing how multiple tasks can acquire locks for different keys concurrently. It highlights the locking mechanism and the asynchronous nature of the operations. This example requires the `System.Threading.Tasks` namespace and assumes a `Log` method for output. ```csharp var collection1 = new KeyedSemaphoresCollection(); var collection2 = new KeyedSemaphoresCollection(); var collection1Tasks = Enumerable.Range(1, 4) .Select(async i => { var key = (int) Math.Ceiling((double)i / 2); Log($"Collection 1 - Task {i:0}: I am waiting for key '{key}'"); using (await collection1.LockAsync(key)) { Log($"Collection 1 - Task {i:0}: Hello world! I have key '{key}' now!"); await Task.Delay(50); } Log($"Collection 1 - Task {i:0}: I have released '{key}'"); }); var collection2Tasks = Enumerable.Range(1, 4) .Select(async i => { var key = (int) Math.Ceiling((double)i / 2); Log($"Collection 2 - Task {i:0}: I am waiting for key '{key}'"); using (await collection2.LockAsync(key)) { Log($"Collection 2 - Task {i:0}: Hello world! I have key '{key}' now!"); await Task.Delay(50); } Log($"Collection 2 - Task {i:0}: I have released '{key}'"); }); await Task.WhenAll(collection1Tasks.Concat(collection2Tasks).AsParallel()); void Log(string message) { Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff} #{Thread.CurrentThread.ManagedThreadId:000} {message}"); } ``` -------------------------------- ### C# Keyed Semaphore Usage Example Source: https://github.com/amoerie/keyed-semaphores/blob/main/README.MD Demonstrates the usage of the `KeyedSemaphore.LockAsync` method in C#. This snippet shows how multiple tasks are initiated, each attempting to acquire a lock for a specific key. It highlights how tasks with the same key are synchronized, while others can proceed in parallel, and logs the state of each task. ```csharp var tasks = Enumerable.Range(1, 4) .Select(async i => { var key = "Key" + Math.Ceiling((double)i / 2); Log($"Task {i:0}: I am waiting for key '{key}'"); using (await KeyedSemaphore.LockAsync(key)) { Log($"Task {i:0}: Hello world! I have key '{key}' now!"); await Task.Delay(50); } Log($"Task {i:0}: I have released '{key}'"); }); await Task.WhenAll(tasks.AsParallel()); void Log(string message) { Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff} #{Thread.CurrentThread.ManagedThreadId:000} {message}"); } ``` -------------------------------- ### IKeyedSemaphoresCollection Interface Usage in C# Source: https://context7.com/amoerie/keyed-semaphores/llms.txt Illustrates the implementation and use of the IKeyedSemaphoresCollection interface in C#. This example shows how to abstract the underlying semaphore collection type (either KeyedSemaphoresCollection or KeyedSemaphoresDictionary) allowing for flexibility in performance tuning. It highlights acquiring locks asynchronously on different collections for parallel processing. ```csharp using System; using System.Threading; using System.Threading.Tasks; using KeyedSemaphores; public class MultiDictionaryProcessor where TKey : notnull { private readonly IKeyedSemaphoresCollection _primaryLocks; private readonly IKeyedSemaphoresCollection _secondaryLocks; public MultiDictionaryProcessor(bool useCollectionForPerformance) { if (useCollectionForPerformance) { _primaryLocks = new KeyedSemaphoresCollection(); _secondaryLocks = new KeyedSemaphoresCollection(); } else { _primaryLocks = new KeyedSemaphoresDictionary(); _secondaryLocks = new KeyedSemaphoresDictionary(); } } public async Task ProcessPrimaryAsync(TKey key) { using (await _primaryLocks.LockAsync(key)) { Console.WriteLine($"Processing in primary: {key}"); await Task.Delay(10); } } public async Task ProcessSecondaryAsync(TKey key) { using (await _secondaryLocks.LockAsync(key)) { Console.WriteLine($"Processing in secondary: {key}"); await Task.Delay(10); } } } // Usage: // var processor = new MultiDictionaryProcessor(useCollectionForPerformance: true); // await Task.WhenAll( // processor.ProcessPrimaryAsync(1), // processor.ProcessSecondaryAsync(1) // Can run in parallel - different dictionaries // ); ``` -------------------------------- ### Instantiate and Use Custom KeyedSemaphoresDictionary in C# Source: https://github.com/amoerie/keyed-semaphores/blob/main/README.MD This C# code snippet demonstrates the creation and usage of two independent `KeyedSemaphoresDictionary` instances, each using integers as keys. It showcases how multiple tasks can concurrently acquire locks on different keys across these separate dictionaries, illustrating the flexibility and isolation provided by custom dictionary instances. The example includes a logging function to visualize the asynchronous operations and lock acquisitions/releases. ```csharp var dictionary1 = new KeyedSemaphoresDictionary(); var dictionary2 = new KeyedSemaphoresDictionary(); var dictionary1Tasks = Enumerable.Range(1, 4) .Select(async i => { var key = (int) Math.Ceiling((double)i / 2); Log($"Dictionary 1 - Task {i:0}: I am waiting for key '{key}'"); using (await dictionary1.LockAsync(key)) { Log($"Dictionary 1 - Task {i:0}: Hello world! I have key '{key}' now!"); await Task.Delay(50); } Log($"Dictionary 1 - Task {i:0}: I have released '{key}'"); }); var dictionary2Tasks = Enumerable.Range(1, 4) .Select(async i => { var key = (int) Math.Ceiling((double)i / 2); Log($"Dictionary 2 - Task {i:0}: I am waiting for key '{key}'"); using (await dictionary2.LockAsync(key)) { Log($"Dictionary 2 - Task {i:0}: Hello world! I have key '{key}' now!"); await Task.Delay(50); } Log($"Dictionary 2 - Task {i:0}: I have released '{key}'"); }); await Task.WhenAll(dictionary1Tasks.Concat(dictionary2Tasks).AsParallel()); void Log(string message) { Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff} #{Thread.CurrentThread.ManagedThreadId:000} {message}"); } ``` -------------------------------- ### KeyedSemaphoresCollection Nested Locking Limitation in C# Source: https://github.com/amoerie/keyed-semaphores/blob/main/README.MD Illustrates the incorrect usage of nested locking with KeyedSemaphoresCollection, which can lead to deadlocks. This example explicitly warns against performing nested asynchronous locks, even with different keys, as they might map to the same underlying semaphore. This code is intended as a negative example. ```csharp // Never do this var collection = new KeyedSemaphoresCollection(); using(await collection.LockAsync("123", cancellationToken)) { using(await collection.LockAsync("456", cancellationToken)) { // Your code here } } ``` -------------------------------- ### KeyedSemaphoresCollection Constructor and LockAsync in C# Source: https://context7.com/amoerie/keyed-semaphores/llms.txt Demonstrates the creation and usage of KeyedSemaphoresCollection for efficient keyed locking in C#. This collection pre-allocates SemaphoreSlim instances, mapping keys to semaphores via hash codes for performance. It is suitable for scenarios with a predictable number of unique keys, offering lower memory allocation compared to dictionary-based approaches. ```csharp using System; using System.Linq; using System.Threading; using System.Threading.Tasks; using KeyedSemaphores; public class HighPerformanceProcessor { private readonly KeyedSemaphoresCollection _collection; public HighPerformanceProcessor(int expectedUniqueKeys) { _collection = new KeyedSemaphoresCollection( numberOfSemaphores: expectedUniqueKeys, synchronousWaitDuration: TimeSpan.FromMilliseconds(0) ); } public async Task ProcessBatchAsync() { var tasks = Enumerable.Range(1, 1000).Select(async i => { var key = $"key-{(i % 100)}"; using (await _collection.LockAsync(key, CancellationToken.None)) { // Critical section - only one task per key can execute this await Task.Delay(1); } }); await Task.WhenAll(tasks); Console.WriteLine("All 1000 tasks completed with efficient keyed locking"); } } // Usage: // var processor = new HighPerformanceProcessor(expectedUniqueKeys: 100); // await processor.ProcessBatchAsync(); // ~2x faster and ~6x less memory allocation compared to KeyedSemaphoresDictionary ``` -------------------------------- ### Initialize and Use KeyedSemaphoresDictionary for Custom Locking (C#) Source: https://context7.com/amoerie/keyed-semaphores/llms.txt Creates and utilizes a dictionary-based keyed semaphores collection. Each unique key maps to its own `SemaphoreSlim` instance with reference counting for automatic cleanup. This allows for managing semaphores for various keys efficiently. ```csharp using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using KeyedSemaphores; public class CustomKeyProcessor { private readonly KeyedSemaphoresDictionary _dictionary; public CustomKeyProcessor() { _dictionary = new KeyedSemaphoresDictionary( concurrencyLevel: Environment.ProcessorCount, capacity: 100, comparer: EqualityComparer.Default, synchronousWaitDuration: TimeSpan.FromMilliseconds(1) ); } public async Task ProcessItemAsync(int itemId) { var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); using (await _dictionary.LockAsync(itemId, cts.Token)) { Console.WriteLine($"Processing item {itemId} on thread {Thread.CurrentThread.ManagedThreadId}"); await Task.Delay(50); Console.WriteLine($"Completed item {itemId}"); } } } // Usage: // var processor = new CustomKeyProcessor(); // var tasks = Enumerable.Range(1, 10).Select(i => processor.ProcessItemAsync(i % 3)); // await Task.WhenAll(tasks); // Items with the same ID (0, 1, 2) will be processed sequentially, others in parallel ``` -------------------------------- ### KeyedSemaphoresDictionary TryLock Synchronous in C# Source: https://context7.com/amoerie/keyed-semaphores/llms.txt Demonstrates the synchronous use of KeyedSemaphoresDictionary's TryLock method in C#. This method attempts to acquire a lock for a given key within a specified timeout and executes a callback action upon successful acquisition. It includes cancellation support and returns a boolean indicating whether the lock was acquired successfully, handling timeouts gracefully. ```csharp using System; using System.Threading; using KeyedSemaphores; public class SyncResourceProcessor { private readonly KeyedSemaphoresDictionary _locks; public SyncResourceProcessor() { _locks = new KeyedSemaphoresDictionary(); } public void ProcessResource(string resourceId) { var timeout = TimeSpan.FromSeconds(3); var cts = new CancellationTokenSource(); bool success = _locks.TryLock( key: resourceId, timeout: timeout, callback: () => { Console.WriteLine($"Processing {resourceId} on thread {Thread.CurrentThread.ManagedThreadId}"); Thread.Sleep(100); Console.WriteLine($"Completed {resourceId}"); }, cancellationToken: cts.Token ); if (!success) { Console.WriteLine($"Timeout acquiring lock for {resourceId}"); } } } // Usage: // var processor = new SyncResourceProcessor(); // Parallel.For(0, 10, i => processor.ProcessResource($"resource-{(i % 3)}")); ``` -------------------------------- ### Acquire Async Keyed Semaphore Lock (C#) Source: https://context7.com/amoerie/keyed-semaphores/llms.txt Acquires a keyed semaphore lock asynchronously using a static singleton instance for a given string key. Returns a disposable object that releases the lock upon disposal. This is useful for managing concurrent access to resources identified by keys, such as bank transactions for different users. ```csharp using System; using System.Linq; using System.Threading; using System.Threading.Tasks; using KeyedSemaphores; public class BankTransactionProcessor { public async Task ProcessTransactionsAsync() { var tasks = Enumerable.Range(1, 4).Select(async i => { var personId = "Person" + Math.Ceiling((double)i / 2); Console.WriteLine($"Task {i}: Waiting for lock on {personId}"); using (await KeyedSemaphore.LockAsync(personId)) { Console.WriteLine($"Task {i}: Processing transaction for {personId}"); await Task.Delay(50); } Console.WriteLine($"Task {i}: Released lock on {personId}"); }); await Task.WhenAll(tasks); } } ``` -------------------------------- ### C# Bank Transaction Processing: Single Lock vs. Keyed Semaphore Source: https://github.com/amoerie/keyed-semaphores/blob/main/README.MD Compares two approaches for processing bank transactions in C#. The old version uses a single lock for all transactions, serializing their execution. The new version utilizes Keyed Semaphores to allow parallel processing for transactions with different person IDs, synchronizing only those with the same ID. ```csharp public class BankTransactionProcessor { private readonly object _lock = new object(); public async Task Process(BankTransaction transaction) { lock(_lock) { // ... processing logic ... } } } ``` ```csharp public class BankTransactionProcessor { public async Task Process(BankTransaction transaction) { var key = transaction.Person.Id.ToString(); using (await KeyedSemaphore.LockAsync(key)) { // ... processing logic ... } } } ``` -------------------------------- ### KeyedSemaphoresCollection with Custom Key Types in C# Source: https://context7.com/amoerie/keyed-semaphores/llms.txt Illustrates creating and using KeyedSemaphoresCollection with a custom record type (UserId) as the key. This provides type-safe locking for concurrent operations on user accounts and transactions, preventing race conditions by acquiring locks based on specific user and transaction IDs. It utilizes nested locks for user and transaction processing. ```csharp using System; using System.Threading.Tasks; using KeyedSemaphores; public record UserId(Guid Id); public class UserAccountProcessor { private readonly KeyedSemaphoresCollection _userLocks; private readonly KeyedSemaphoresDictionary _transactionLocks; public UserAccountProcessor() { _userLocks = new KeyedSemaphoresCollection(numberOfSemaphores: 10000); _transactionLocks = new KeyedSemaphoresDictionary(); } public async Task ProcessUserTransactionAsync(UserId userId, Guid transactionId) { // Lock on user ID to prevent concurrent modifications to same user using (await _userLocks.LockAsync(userId)) { Console.WriteLine($"Processing user {userId.Id}"); // Lock on transaction ID (different dictionary, no collision) using (await _transactionLocks.LockAsync(transactionId)) { Console.WriteLine($"Processing transaction {transactionId}"); await Task.Delay(50); } } Console.WriteLine($"Completed transaction {transactionId} for user {userId.Id}"); } } // Usage: // var processor = new UserAccountProcessor(); // var userId = new UserId(Guid.NewGuid()); // await processor.ProcessUserTransactionAsync(userId, Guid.NewGuid()); ``` -------------------------------- ### KeyedSemaphoresCollection Constructor Source: https://github.com/amoerie/keyed-semaphores/blob/main/README.MD Initializes a new instance of the KeyedSemaphoresCollection with a specified number of semaphores. This is useful when the number of unique keys is known beforehand to optimize semaphore allocation. ```APIDOC ## KeyedSemaphoresCollection Constructor ### Description Initializes a new instance of the KeyedSemaphoresCollection with a specified number of semaphores. Each key internally maps to a semaphore. If the number of unique keys exceeds the number of semaphores, different keys might share the same semaphore, potentially affecting concurrency. ### Method Constructor ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **numberOfSemaphores** (int) - Required - The total number of semaphores to be used by the collection. This value should ideally reflect or be greater than the expected number of unique keys. ### Request Example ```csharp int numberOfUniqueKeys = 25000; var collection = new KeyedSemaphoresCollection(numberOfUniqueKeys); ``` ### Response #### Success Response (Constructor) N/A (Constructor does not return a value in the traditional sense, but initializes an object.) #### Response Example N/A ``` -------------------------------- ### TryLock with Timeout and Callback Source: https://github.com/amoerie/keyed-semaphores/blob/main/README.MD Attempts to acquire a lock for a given key within a specified timeout period. If successful, a callback action or function is executed. Supports cancellation. ```APIDOC ## TryLock with Timeout and Callback ### Description Attempts to acquire a lock for a specified key within a given timeout. If the lock is acquired successfully, a provided callback (either synchronous or asynchronous) is executed. The method also supports cancellation. ### Method - `bool TryLock(TKey key, TimeSpan timeout, Action callback, CancellationToken cancellationToken = default)` - `Task TryLockAsync(TKey key, TimeSpan timeout, Action callback, CancellationToken cancellationToken = default)` - `Task TryLockAsync(TKey key, TimeSpan timeout, Func callback, CancellationToken cancellationToken = default)` ### Endpoint N/A (These are methods on the `IKeyedSemaphoresCollection` interface) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **key** (TKey) - Required - The key for which to attempt to acquire the lock. - **timeout** (TimeSpan) - Required - The maximum time to wait for the lock to be acquired. - **callback** (Action or Func) - Required - The action or asynchronous function to execute if the lock is acquired. For `TryLockAsync` with `Func`, the callback must be an async method. - **cancellationToken** (CancellationToken) - Optional - A token that can be used to cancel the operation. ### Request Example ```csharp CancellationToken cancellationToken = ...; if(await KeyedSemaphore.TryLockAsync("123", TimeSpan.FromSeconds(10), CallbackAsync, cancellationToken)) { Console.WriteLine("Lock was acquired within 10 seconds!"); } async Task CallbackAsync() { // Your code here } ``` ### Response #### Success Response (200 OK) - **bool** - Returns `true` if the lock was acquired and the callback was executed, `false` otherwise (e.g., if the timeout expired or the operation was canceled before acquiring the lock). #### Response Example ```json { "result": true } ``` **Note**: The callback's execution is part of the result; if the callback throws an exception, it will propagate. ``` -------------------------------- ### Timeout and Cancel Lock Acquisition in KeyedSemaphoresCollection Source: https://github.com/amoerie/keyed-semaphores/blob/main/README.MD Shows how to acquire a lock with both timeout and cancellation. The TryLock and TryLockAsync methods take a TimeSpan for the timeout and a CancellationToken. The lock-constrained code is provided via a callback. ```csharp interface IKeyedSemaphoresCollection { bool TryLock(TKey key, TimeSpan timeout, Action callback, CancellationToken cancellationToken = default) Task TryLockAsync(TKey key, TimeSpan timeout, Action callback, CancellationToken cancellationToken = default) Task TryLockAsync(TKey key, TimeSpan timeout, Func callback, CancellationToken cancellationToken = default) } // Usage example: CancellationToken cancellationToken = ...; if(await KeyedSemaphore.TryLockAsync("123", TimeSpan.FromSeconds(10), CallbackAsync, cancellationToken)) { Console.WriteLine("Lock was acquired within 10 seconds!"); } async Task CallbackAsync() { // Your code here } ``` -------------------------------- ### Acquire Synchronous Keyed Semaphore Lock (C#) Source: https://context7.com/amoerie/keyed-semaphores/llms.txt Synchronously acquires a keyed semaphore lock for a specified string key, blocking the current thread until the lock is available. This method is suitable for scenarios where blocking is acceptable, such as managing directory creation where operations for the same root directory should be sequential. ```csharp using System; using System.Threading; using KeyedSemaphores; public class DirectoryManager { public void CreateDirectory(string path) { var key = GetRootDirectory(path); using (KeyedSemaphore.Lock(key)) { Console.WriteLine($"Creating directory: {path}"); System.IO.Directory.CreateDirectory(path); Thread.Sleep(10); } Console.WriteLine($"Directory created: {path}"); } private string GetRootDirectory(string path) { return path.Split('/')[0]; } } ``` -------------------------------- ### Try Acquire Async Keyed Semaphore Lock with Callback and Timeout (C#) Source: https://context7.com/amoerie/keyed-semaphores/llms.txt Attempts to acquire a keyed semaphore lock asynchronously within a specified timeout. If the lock is acquired, it executes a provided callback action. Returns a boolean indicating whether the lock was successfully acquired. This is useful for operations like cache updates where a timeout is necessary to prevent indefinite waiting. ```csharp using System; using System.Threading; using System.Threading.Tasks; using KeyedSemaphores; public class CacheManager { public async Task UpdateCacheAsync(string cacheKey, Func updateAction) { var timeout = TimeSpan.FromSeconds(5); var cts = new CancellationTokenSource(); bool lockAcquired = await KeyedSemaphore.TryLockAsync( cacheKey, timeout, async () => { Console.WriteLine($"Lock acquired for {cacheKey}"); await updateAction(); Console.WriteLine($"Cache updated for {cacheKey}"); }, cts.Token); if (!lockAcquired) { Console.WriteLine($"Failed to acquire lock for {cacheKey} within {timeout}"); } return lockAcquired; } } ``` -------------------------------- ### Initialize KeyedSemaphoresCollection with Number of Semaphores Source: https://github.com/amoerie/keyed-semaphores/blob/main/README.MD Initializes a KeyedSemaphoresCollection with a specified number of semaphores. This is useful when the number of unique keys is known beforehand to optimize semaphore distribution. ```csharp int numberOfUniqueKeys = 25000; var collection = new KeyedSemaphoresCollection(numberOfUniqueKeys); ``` -------------------------------- ### TryLockAsync with Timeout and Cancellation (No Callback) Source: https://github.com/amoerie/keyed-semaphores/blob/main/README.MD Attempts to asynchronously acquire a lock for a given key within a specified timeout and cancellation token, returning a nullable IDisposable. The caller must dispose the returned object if it's not null. ```APIDOC ## TryLockAsync with Timeout and Cancellation (No Callback) ### Description Asynchronously attempts to acquire a lock for a specific key within a given timeout period, also supporting cancellation. This method returns a nullable `IDisposable`. If the lock is successfully acquired, the returned object should be disposed to release the lock. It is recommended to use a null-check pattern for proper handling. ### Method - `ValueTask TryLockAsync(TKey key, TimeSpan timeout, CancellationToken cancellationToken = default)` ### Endpoint N/A (This is a method on the `IKeyedSemaphoresCollection` interface) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **key** (TKey) - Required - The key for which to attempt to acquire the lock. - **timeout** (TimeSpan) - Required - The maximum time to wait for the lock to be acquired. - **cancellationToken** (CancellationToken) - Optional - A token that can be used to cancel the operation. ### Request Example ```csharp using var lockScope = await KeyedSemaphore.TryLockAsync("123", TimeSpan.FromSeconds(42), cancellationToken); if (lockScope is not null) { // Return from your critical section here } // Return from your non critical section here ``` ### Response #### Success Response (200 OK) - **IDisposable?** - A nullable `IDisposable` object. If not null, it represents the acquired lock and must be disposed. If null, the lock was not acquired within the timeout or due to cancellation. ``` -------------------------------- ### Lock with Cancellation Only Source: https://github.com/amoerie/keyed-semaphores/blob/main/README.MD Acquires a lock for a given key, supporting cancellation. If the cancellation token is signaled before the lock is acquired, an OperationCanceledException is thrown. ```APIDOC ## Lock with Cancellation Only ### Description Acquires a lock for a specific key, allowing for cancellation via a `CancellationToken`. If the operation is canceled before the lock is obtained, an `OperationCanceledException` will be thrown. ### Method - `IDisposable Lock(TKey key, CancellationToken cancellationToken = default)` - `Task LockAsync(TKey key, CancellationToken cancellationToken = default)` ### Endpoint N/A (These are methods on the `IKeyedSemaphoresCollection` interface) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **key** (TKey) - Required - The key for which to acquire the lock. - **cancellationToken** (CancellationToken) - Optional - A token that can be used to cancel the asynchronous operation. ### Request Example ```csharp CancellationToken cancellationToken = ...; using(await KeyedSemaphore.LockAsync("123", cancellationToken)) { // Your code here } ``` ### Response #### Success Response (200 OK) - **IDisposable** - An IDisposable object representing the acquired lock. This must be disposed to release the lock. #### Response Example N/A (This is a synchronous or asynchronous method returning an object.) ``` -------------------------------- ### Cancel Lock Acquisition in KeyedSemaphoresCollection Source: https://github.com/amoerie/keyed-semaphores/blob/main/README.MD Demonstrates how to acquire a lock with cancellation support. The Lock and LockAsync methods accept a CancellationToken, which can be used to abort the lock acquisition process by throwing an OperationCanceledException. ```csharp interface IKeyedSemaphoresCollection { IDisposable Lock(TKey key, CancellationToken cancellationToken = default) Task LockAsync(TKey key, CancellationToken cancellationToken = default) } // Usage example: CancellationToken cancellationToken = ...; using(await KeyedSemaphore.LockAsync("123", cancellationToken)) { // Your code here } ``` -------------------------------- ### Attempt Keyed Semaphore Lock with Timeout (C#) Source: https://context7.com/amoerie/keyed-semaphores/llms.txt Attempts to acquire a keyed semaphore lock within a specified timeout. It returns a nullable IDisposable which must be checked for null and disposed if it is not null. This is useful for scenarios where blocking indefinitely is not desired. ```csharp using System; using System.Threading; using System.Threading.Tasks; using KeyedSemapaphores; public class ResourceManager { public async Task ProcessResourceAsync(string resourceId, CancellationToken cancellationToken) { using var lockScope = await KeyedSemaphore.TryLockAsync( resourceId, TimeSpan.FromSeconds(10), cancellationToken); if (lockScope is null) { Console.WriteLine($"Could not acquire lock for {resourceId} within timeout"); return false; } Console.WriteLine($"Processing resource {resourceId}"); await Task.Delay(100, cancellationToken); Console.WriteLine($"Resource {resourceId} processed successfully"); return true; } } // Usage: // var manager = new ResourceManager(); // var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); // bool result = await manager.ProcessResourceAsync("resource:456", cts.Token); ``` -------------------------------- ### Advanced Timeout & Cancellation without Callback in KeyedSemaphoresCollection Source: https://github.com/amoerie/keyed-semaphores/blob/main/README.MD Demonstrates acquiring a lock with timeout and cancellation without using a callback. The TryLockAsync method returns a nullable IDisposable, which must be checked for null and disposed if not null, following a null-check pattern for correct behavior. ```csharp interface IKeyedSemaphoresCollection { ValueTask TryLockAsync(TKey key, TimeSpan timeout, CancellationToken cancellationToken = default) } // Usage example: using var lockScope = await KeyedSemaphore.TryLockAsync("123", TimeSpan.FromSeconds(42), cancellationToken); if (lockScope is not null) { // Return from your critical section here } // Return from your non critical section here ``` -------------------------------- ### Check if Keyed Semaphore is In Use (C#) Source: https://context7.com/amoerie/keyed-semaphores/llms.txt Checks whether a specified key is currently locked by any thread. It returns `true` if the semaphore associated with the key is held and `false` if it is available. This method is useful for monitoring lock status without attempting to acquire the lock. ```csharp using System; using System.Threading.Tasks; using KeyedSemaphores; public class LockMonitor { public async Task MonitorLockStatusAsync(string key) { Console.WriteLine($"Initial status - Key '{key}' in use: {KeyedSemaphore.IsInUse(key)}"); using (await KeyedSemaphore.LockAsync(key)) { Console.WriteLine($"Inside lock - Key '{key}' in use: {KeyedSemaphore.IsInUse(key)}"); await Task.Delay(100); } Console.WriteLine($"After release - Key '{key}' in use: {KeyedSemaphore.IsInUse(key)}"); } } // Output: // Initial status - Key 'test-key' in use: False // Inside lock - Key 'test-key' in use: True // After release - Key 'test-key' in use: False ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.