KeyedLock<TKey> Class

Provides a synchronous lock mechanism that locks based on a key, allowing concurrent operations on different keys.

public sealed class KeyedLock<TKey> where TKey : notnull

Type parameters

TKey
The type of the key.

Remarks

The lock is thread-affine: it is built on Lock, which requires the lease to be released on the thread that acquired it. The critical section must therefore stay on one thread and must not await. Use KeyedAsyncLock<TKey> for asynchronous code.

Example

var keyedLock = new KeyedLock<string>(StringComparer.Ordinal);
var updateCounts = new ConcurrentDictionary<string, int>(StringComparer.Ordinal);

void ProcessUser(string userId)
{
    using (keyedLock.Lock(userId))
    {
        // Only one operation per userId at a time
        // Multiple different userIds can be processed concurrently
        var count = updateCounts.GetValueOrDefault(userId);
        updateCounts[userId] = count + 1;
    }
}

Parallel.Invoke(() => ProcessUser("alice"), () => ProcessUser("alice"), () => ProcessUser("bob"));
Assert.Equal(2, updateCounts["alice"]);
Assert.Equal(1, updateCounts["bob"]);

Constructors

NameDescription
KeyedLock()Initializes a new instance of the KeyedLock<TKey> class.
KeyedLock(IEqualityComparer<TKey>?)Initializes a new instance of the KeyedLock<TKey> class with the specified equality comparer.

KeyedLock overloads

KeyedLock()

KeyedLock.cs

Initializes a new instance of the KeyedLock<TKey> class.

public KeyedLock()

KeyedLock(IEqualityComparer<TKey>?)

KeyedLock.cs

Initializes a new instance of the KeyedLock<TKey> class with the specified equality comparer.

public KeyedLock(IEqualityComparer<TKey>? comparer)
Parameters
comparer IEqualityComparer<TKey>?
The equality comparer to use when comparing keys.

Methods

NameDescription
Lock(TKey)Acquires the lock for the specified key.

Lock(TKey)

KeyedLock.cs

Acquires the lock for the specified key.

public IDisposable Lock(TKey key)

Parameters

key TKey
The key to lock on.

Returns

IDisposable A disposable object. Disposing the object releases the lock.

Remarks

The returned object must be disposed on the thread that called this method. Disposing it on another thread throws SynchronizationLockException and leaves the underlying lock held; the object is not consumed by such a failed disposal, so the owning thread can still dispose it to release the lock.