diff --git a/AsyncKeyedLock/AsyncKeyedLock.csproj b/AsyncKeyedLock/AsyncKeyedLock.csproj index 5fab03b..b620c7e 100644 --- a/AsyncKeyedLock/AsyncKeyedLock.csproj +++ b/AsyncKeyedLock/AsyncKeyedLock.csproj @@ -8,16 +8,16 @@ https://github.com/MarkCiliaVincenti/AsyncKeyedLock MIT MIT - 6.2.5 + 6.2.6 logo.png - Minor optimization. + Cleaning pooling and aggressively inlining locking methods. An asynchronous .NET Standard 2.0 library that allows you to lock based on a key (keyed semaphores), limiting concurrent threads sharing the same key to a specified number, with optional pooling for reducing memory allocations. - © 2023 Mark Cilia Vincenti + © 2024 Mark Cilia Vincenti async,lock,key,keyed,semaphore,striped,dictionary,concurrentdictionary,pooling,duplicate,synchronization git false - 6.2.5.0 - 6.2.5.0 + 6.2.6.0 + 6.2.6.0 README.md true True diff --git a/AsyncKeyedLock/AsyncKeyedLockDictionary.cs b/AsyncKeyedLock/AsyncKeyedLockDictionary.cs index 1839d6f..4de2ca4 100644 --- a/AsyncKeyedLock/AsyncKeyedLockDictionary.cs +++ b/AsyncKeyedLock/AsyncKeyedLockDictionary.cs @@ -24,7 +24,7 @@ public AsyncKeyedLockDictionary(AsyncKeyedLockOptions options) : base() if (options.PoolSize > 0) { PoolingEnabled = true; - _pool = new AsyncKeyedLockPool((key) => new AsyncKeyedLockReleaser(key, new SemaphoreSlim(MaxCount, MaxCount), this), options.PoolSize, options.PoolInitialFill); + _pool = new AsyncKeyedLockPool(this, options.PoolSize, options.PoolInitialFill); } } @@ -40,7 +40,7 @@ public AsyncKeyedLockDictionary(AsyncKeyedLockOptions options, IEqualityComparer if (options.PoolSize > 0) { PoolingEnabled = true; - _pool = new AsyncKeyedLockPool((key) => new AsyncKeyedLockReleaser(key, new SemaphoreSlim(MaxCount, MaxCount), this), options.PoolSize, options.PoolInitialFill); + _pool = new AsyncKeyedLockPool(this, options.PoolSize, options.PoolInitialFill); } } @@ -56,7 +56,7 @@ public AsyncKeyedLockDictionary(AsyncKeyedLockOptions options, int concurrencyLe if (options.PoolSize > 0) { PoolingEnabled = true; - _pool = new AsyncKeyedLockPool((key) => new AsyncKeyedLockReleaser(key, new SemaphoreSlim(MaxCount, MaxCount), this), options.PoolSize, options.PoolInitialFill); + _pool = new AsyncKeyedLockPool(this, options.PoolSize, options.PoolInitialFill); } } @@ -72,7 +72,7 @@ public AsyncKeyedLockDictionary(AsyncKeyedLockOptions options, int concurrencyLe if (options.PoolSize > 0) { PoolingEnabled = true; - _pool = new AsyncKeyedLockPool((key) => new AsyncKeyedLockReleaser(key, new SemaphoreSlim(MaxCount, MaxCount), this), options.PoolSize, options.PoolInitialFill); + _pool = new AsyncKeyedLockPool(this, options.PoolSize, options.PoolInitialFill); } } diff --git a/AsyncKeyedLock/AsyncKeyedLockOptions.cs b/AsyncKeyedLock/AsyncKeyedLockOptions.cs index 1f47b73..36677fc 100644 --- a/AsyncKeyedLock/AsyncKeyedLockOptions.cs +++ b/AsyncKeyedLock/AsyncKeyedLockOptions.cs @@ -11,7 +11,9 @@ public sealed class AsyncKeyedLockOptions public int MaxCount { get; set; } = 1; /// - /// The size of the pool to use in order for generated objects to be reused. Defaults to 0 (disabled). + /// The size of the pool to use in order for generated objects to be reused. This is NOT a concurrency limit, + /// but if the pool is empty then a new object will be created rather than waiting for an object to return to + /// the pool. Defaults to 0 (disabled) but strongly recommended to use. /// public int PoolSize { get; set; } = 0; @@ -24,7 +26,9 @@ public sealed class AsyncKeyedLockOptions /// Initializes options for the constructors /// /// The maximum number of requests for the semaphore that can be granted concurrently. Defaults to 1. - /// The size of the pool to use in order for generated objects to be reused. Defaults to 0 (disabled). + /// The size of the pool to use in order for generated objects to be reused. This is NOT a concurrency limit, + /// but if the pool is empty then a new object will be created rather than waiting for an object to return to + /// the pool. Defaults to 0 (disabled) but strongly recommended to use. /// The number of items to fill the pool with during initialization. Defaults to -1 (fill up to pool size). public AsyncKeyedLockOptions(int maxCount = 1, int poolSize = 0, int poolInitialFill = -1) { diff --git a/AsyncKeyedLock/AsyncKeyedLockPool.cs b/AsyncKeyedLock/AsyncKeyedLockPool.cs index 6f8aa64..10aa907 100644 --- a/AsyncKeyedLock/AsyncKeyedLockPool.cs +++ b/AsyncKeyedLock/AsyncKeyedLockPool.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Concurrent; using System.Runtime.CompilerServices; +using System.Threading; namespace AsyncKeyedLock { @@ -9,10 +10,14 @@ internal sealed class AsyncKeyedLockPool : IDisposable private readonly BlockingCollection> _objects; private readonly Func> _objectGenerator; - public AsyncKeyedLockPool(Func> objectGenerator, int capacity, int initialFill = -1) + public AsyncKeyedLockPool(AsyncKeyedLockDictionary asyncKeyedLockDictionary, int capacity, int initialFill = -1) { _objects = new BlockingCollection>(new ConcurrentBag>(), capacity); - _objectGenerator = objectGenerator; + _objectGenerator = (key) => new AsyncKeyedLockReleaser( + key, + new SemaphoreSlim(asyncKeyedLockDictionary.MaxCount, asyncKeyedLockDictionary.MaxCount), + asyncKeyedLockDictionary); + if (initialFill < 0) { for (int i = 0; i < capacity; ++i) diff --git a/AsyncKeyedLock/AsyncKeyedLocker.cs b/AsyncKeyedLock/AsyncKeyedLocker.cs index e4b06aa..d5a81d0 100644 --- a/AsyncKeyedLock/AsyncKeyedLocker.cs +++ b/AsyncKeyedLock/AsyncKeyedLocker.cs @@ -335,6 +335,7 @@ public AsyncKeyedLocker(Action options, int concurrencyLe /// /// The key to lock on. /// A disposable value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public IDisposable Lock(TKey key) { var releaser = GetOrAdd(key); @@ -348,6 +349,7 @@ public IDisposable Lock(TKey key) /// The key to lock on. /// The to observe. /// A disposable value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public IDisposable Lock(TKey key, CancellationToken cancellationToken) { var releaser = GetOrAdd(key); @@ -370,6 +372,7 @@ public IDisposable Lock(TKey key, CancellationToken cancellationToken) /// The number of milliseconds to wait, (-1) to wait indefinitely, or zero to test the state of the wait handle and return immediately. /// An out parameter showing whether or not the semaphore was entered. /// A disposable value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public IDisposable Lock(TKey key, int millisecondsTimeout, out bool entered) { var releaser = GetOrAdd(key); @@ -390,6 +393,7 @@ public IDisposable Lock(TKey key, int millisecondsTimeout, out bool entered) /// A that represents the number of milliseconds to wait, a that represents -1 milliseconds to wait indefinitely, or a that represents 0 milliseconds to test the wait handle and return immediately. /// An out parameter showing whether or not the semaphore was entered. /// A disposable value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public IDisposable Lock(TKey key, TimeSpan timeout, out bool entered) { var releaser = GetOrAdd(key); @@ -411,6 +415,7 @@ public IDisposable Lock(TKey key, TimeSpan timeout, out bool entered) /// The to observe. /// An out parameter showing whether or not the semaphore was entered. /// A disposable value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public IDisposable Lock(TKey key, int millisecondsTimeout, CancellationToken cancellationToken, out bool entered) { var releaser = GetOrAdd(key); @@ -441,6 +446,7 @@ public IDisposable Lock(TKey key, int millisecondsTimeout, CancellationToken can /// The to observe. /// An out parameter showing whether or not the semaphore was entered. /// A disposable value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public IDisposable Lock(TKey key, TimeSpan timeout, CancellationToken cancellationToken, out bool entered) { var releaser = GetOrAdd(key); @@ -472,6 +478,7 @@ public IDisposable Lock(TKey key, TimeSpan timeout, CancellationToken cancellati /// The synchronous action. /// The number of milliseconds to wait, (-1) to wait indefinitely, or zero to test the state of the wait handle and return immediately. /// False if timed out, true if it successfully entered. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryLock(TKey key, Action action, int millisecondsTimeout) { var releaser = GetOrAdd(key); @@ -499,6 +506,7 @@ public bool TryLock(TKey key, Action action, int millisecondsTimeout) /// The synchronous action. /// A that represents the number of milliseconds to wait, a that represents -1 milliseconds to wait indefinitely, or a that represents 0 milliseconds to test the wait handle and return immediately. /// False if timed out, true if it successfully entered. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryLock(TKey key, Action action, TimeSpan timeout) { var releaser = GetOrAdd(key); @@ -527,6 +535,7 @@ public bool TryLock(TKey key, Action action, TimeSpan timeout) /// The number of milliseconds to wait, (-1) to wait indefinitely, or zero to test the state of the wait handle and return immediately. /// The to observe. /// False if timed out, true if it successfully entered. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryLock(TKey key, Action action, int millisecondsTimeout, CancellationToken cancellationToken) { var releaser = GetOrAdd(key); @@ -563,6 +572,7 @@ public bool TryLock(TKey key, Action action, int millisecondsTimeout, Cancellati /// A that represents the number of milliseconds to wait, a that represents -1 milliseconds to wait indefinitely, or a that represents 0 milliseconds to test the wait handle and return immediately. /// The to observe. /// False if timed out, true if it successfully entered. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryLock(TKey key, Action action, TimeSpan timeout, CancellationToken cancellationToken) { var releaser = GetOrAdd(key); @@ -601,6 +611,7 @@ public bool TryLock(TKey key, Action action, TimeSpan timeout, CancellationToken /// The number of milliseconds to wait, (-1) to wait indefinitely, or zero to test the state of the wait handle and return immediately. /// true to attempt to marshal the continuation back to the original context captured; otherwise, false. Defaults to false. /// False if timed out, true if it successfully entered. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public async ValueTask TryLockAsync(TKey key, Action action, int millisecondsTimeout, bool continueOnCapturedContext = false) { var releaser = GetOrAdd(key); @@ -629,6 +640,7 @@ public async ValueTask TryLockAsync(TKey key, Action action, int milliseco /// The number of milliseconds to wait, (-1) to wait indefinitely, or zero to test the state of the wait handle and return immediately. /// true to attempt to marshal the continuation back to the original context captured; otherwise, false. Defaults to false. /// False if timed out, true if it successfully entered. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public async ValueTask TryLockAsync(TKey key, Func task, int millisecondsTimeout, bool continueOnCapturedContext = false) { var releaser = GetOrAdd(key); @@ -657,6 +669,7 @@ public async ValueTask TryLockAsync(TKey key, Func task, int millise /// A that represents the number of milliseconds to wait, a that represents -1 milliseconds to wait indefinitely, or a that represents 0 milliseconds to test the wait handle and return immediately. /// true to attempt to marshal the continuation back to the original context captured; otherwise, false. Defaults to false. /// False if timed out, true if it successfully entered. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public async ValueTask TryLockAsync(TKey key, Action action, TimeSpan timeout, bool continueOnCapturedContext = false) { var releaser = GetOrAdd(key); @@ -685,6 +698,7 @@ public async ValueTask TryLockAsync(TKey key, Action action, TimeSpan time /// A that represents the number of milliseconds to wait, a that represents -1 milliseconds to wait indefinitely, or a that represents 0 milliseconds to test the wait handle and return immediately. /// true to attempt to marshal the continuation back to the original context captured; otherwise, false. Defaults to false. /// False if timed out, true if it successfully entered. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public async ValueTask TryLockAsync(TKey key, Func task, TimeSpan timeout, bool continueOnCapturedContext = false) { var releaser = GetOrAdd(key); @@ -714,6 +728,7 @@ public async ValueTask TryLockAsync(TKey key, Func task, TimeSpan ti /// The to observe. /// true to attempt to marshal the continuation back to the original context captured; otherwise, false. Defaults to false. /// False if timed out, true if it successfully entered. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public async ValueTask TryLockAsync(TKey key, Action action, int millisecondsTimeout, CancellationToken cancellationToken, bool continueOnCapturedContext = false) { var releaser = GetOrAdd(key); @@ -751,6 +766,7 @@ public async ValueTask TryLockAsync(TKey key, Action action, int milliseco /// The to observe. /// true to attempt to marshal the continuation back to the original context captured; otherwise, false. Defaults to false. /// False if timed out, true if it successfully entered. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public async ValueTask TryLockAsync(TKey key, Func task, int millisecondsTimeout, CancellationToken cancellationToken, bool continueOnCapturedContext = false) { var releaser = GetOrAdd(key); @@ -788,6 +804,7 @@ public async ValueTask TryLockAsync(TKey key, Func task, int millise /// The to observe. /// true to attempt to marshal the continuation back to the original context captured; otherwise, false. Defaults to false. /// False if timed out, true if it successfully entered. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public async ValueTask TryLockAsync(TKey key, Action action, TimeSpan timeout, CancellationToken cancellationToken, bool continueOnCapturedContext = false) { var releaser = GetOrAdd(key); @@ -825,6 +842,7 @@ public async ValueTask TryLockAsync(TKey key, Action action, TimeSpan time /// The to observe. /// true to attempt to marshal the continuation back to the original context captured; otherwise, false. Defaults to false. /// False if timed out, true if it successfully entered. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public async ValueTask TryLockAsync(TKey key, Func task, TimeSpan timeout, CancellationToken cancellationToken, bool continueOnCapturedContext = false) { var releaser = GetOrAdd(key); @@ -864,6 +882,7 @@ public async ValueTask TryLockAsync(TKey key, Func task, TimeSpan ti /// The number of milliseconds to wait, (-1) to wait indefinitely, or zero to test the state of the wait handle and return immediately. /// Options used to configure how awaits on this task are performed. /// False if timed out, true if it successfully entered. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public async ValueTask TryLockAsync(TKey key, Action action, int millisecondsTimeout, ConfigureAwaitOptions configureAwaitOptions) { var releaser = GetOrAdd(key); @@ -892,6 +911,7 @@ public async ValueTask TryLockAsync(TKey key, Action action, int milliseco /// The number of milliseconds to wait, (-1) to wait indefinitely, or zero to test the state of the wait handle and return immediately. /// Options used to configure how awaits on this task are performed. /// False if timed out, true if it successfully entered. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public async ValueTask TryLockAsync(TKey key, Func task, int millisecondsTimeout, ConfigureAwaitOptions configureAwaitOptions) { var releaser = GetOrAdd(key); @@ -920,6 +940,7 @@ public async ValueTask TryLockAsync(TKey key, Func task, int millise /// A that represents the number of milliseconds to wait, a that represents -1 milliseconds to wait indefinitely, or a that represents 0 milliseconds to test the wait handle and return immediately. /// Options used to configure how awaits on this task are performed. /// False if timed out, true if it successfully entered. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public async ValueTask TryLockAsync(TKey key, Action action, TimeSpan timeout, ConfigureAwaitOptions configureAwaitOptions) { var releaser = GetOrAdd(key); @@ -948,6 +969,7 @@ public async ValueTask TryLockAsync(TKey key, Action action, TimeSpan time /// A that represents the number of milliseconds to wait, a that represents -1 milliseconds to wait indefinitely, or a that represents 0 milliseconds to test the wait handle and return immediately. /// Options used to configure how awaits on this task are performed. /// False if timed out, true if it successfully entered. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public async ValueTask TryLockAsync(TKey key, Func task, TimeSpan timeout, ConfigureAwaitOptions configureAwaitOptions) { var releaser = GetOrAdd(key); @@ -977,6 +999,7 @@ public async ValueTask TryLockAsync(TKey key, Func task, TimeSpan ti /// The to observe. /// Options used to configure how awaits on this task are performed. /// False if timed out, true if it successfully entered. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public async ValueTask TryLockAsync(TKey key, Action action, int millisecondsTimeout, CancellationToken cancellationToken, ConfigureAwaitOptions configureAwaitOptions) { var releaser = GetOrAdd(key); @@ -1014,6 +1037,7 @@ public async ValueTask TryLockAsync(TKey key, Action action, int milliseco /// The to observe. /// Options used to configure how awaits on this task are performed. /// False if timed out, true if it successfully entered. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public async ValueTask TryLockAsync(TKey key, Func task, int millisecondsTimeout, CancellationToken cancellationToken, ConfigureAwaitOptions configureAwaitOptions) { var releaser = GetOrAdd(key); @@ -1051,6 +1075,7 @@ public async ValueTask TryLockAsync(TKey key, Func task, int millise /// The to observe. /// Options used to configure how awaits on this task are performed. /// False if timed out, true if it successfully entered. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public async ValueTask TryLockAsync(TKey key, Action action, TimeSpan timeout, CancellationToken cancellationToken, ConfigureAwaitOptions configureAwaitOptions) { var releaser = GetOrAdd(key); @@ -1088,6 +1113,7 @@ public async ValueTask TryLockAsync(TKey key, Action action, TimeSpan time /// The to observe. /// Options used to configure how awaits on this task are performed. /// False if timed out, true if it successfully entered. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public async ValueTask TryLockAsync(TKey key, Func task, TimeSpan timeout, CancellationToken cancellationToken, ConfigureAwaitOptions configureAwaitOptions) { var releaser = GetOrAdd(key); @@ -1125,6 +1151,7 @@ public async ValueTask TryLockAsync(TKey key, Func task, TimeSpan ti /// The key to lock on. /// true to attempt to marshal the continuation back to the original context captured; otherwise, false. Defaults to false. /// A disposable value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public async ValueTask LockAsync(TKey key, bool continueOnCapturedContext = false) { var releaser = GetOrAdd(key); @@ -1139,6 +1166,7 @@ public async ValueTask LockAsync(TKey key, bool continueOnCapturedC /// The to observe. /// true to attempt to marshal the continuation back to the original context captured; otherwise, false. Defaults to false. /// A disposable value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public async ValueTask LockAsync(TKey key, CancellationToken cancellationToken, bool continueOnCapturedContext = false) { var releaser = GetOrAdd(key); @@ -1161,6 +1189,7 @@ public async ValueTask LockAsync(TKey key, CancellationToken cancel /// The number of milliseconds to wait, (-1) to wait indefinitely, or zero to test the state of the wait handle and return immediately. /// true to attempt to marshal the continuation back to the original context captured; otherwise, false. Defaults to false. /// A disposable value of type . + [MethodImpl(MethodImplOptions.AggressiveInlining)] public async ValueTask> LockAsync(TKey key, int millisecondsTimeout, bool continueOnCapturedContext = false) { var releaser = GetOrAdd(key); @@ -1174,6 +1203,7 @@ public async ValueTask> LockAsync(TKey key, /// A that represents the number of milliseconds to wait, a that represents -1 milliseconds to wait indefinitely, or a that represents 0 milliseconds to test the wait handle and return immediately. /// true to attempt to marshal the continuation back to the original context captured; otherwise, false. Defaults to false. /// A disposable value of type . + [MethodImpl(MethodImplOptions.AggressiveInlining)] public async ValueTask> LockAsync(TKey key, TimeSpan timeout, bool continueOnCapturedContext = false) { var releaser = GetOrAdd(key); @@ -1188,6 +1218,7 @@ public async ValueTask> LockAsync(TKey key, /// The to observe. /// true to attempt to marshal the continuation back to the original context captured; otherwise, false. Defaults to false. /// A disposable value of type . + [MethodImpl(MethodImplOptions.AggressiveInlining)] public async ValueTask> LockAsync(TKey key, int millisecondsTimeout, CancellationToken cancellationToken, bool continueOnCapturedContext = false) { var releaser = GetOrAdd(key); @@ -1210,6 +1241,7 @@ public async ValueTask> LockAsync(TKey key, /// The to observe. /// true to attempt to marshal the continuation back to the original context captured; otherwise, false. Defaults to false. /// A disposable value of type . + [MethodImpl(MethodImplOptions.AggressiveInlining)] public async ValueTask> LockAsync(TKey key, TimeSpan timeout, CancellationToken cancellationToken, bool continueOnCapturedContext = false) { var releaser = GetOrAdd(key); @@ -1233,6 +1265,7 @@ public async ValueTask> LockAsync(TKey key, /// The key to lock on. /// Options used to configure how awaits on this task are performed. /// A disposable value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public async ValueTask LockAsync(TKey key, ConfigureAwaitOptions configureAwaitOptions) { var releaser = GetOrAdd(key); @@ -1247,6 +1280,7 @@ public async ValueTask LockAsync(TKey key, ConfigureAwaitOptions co /// The to observe. /// Options used to configure how awaits on this task are performed. /// A disposable value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public async ValueTask LockAsync(TKey key, CancellationToken cancellationToken, ConfigureAwaitOptions configureAwaitOptions) { var releaser = GetOrAdd(key); @@ -1269,6 +1303,7 @@ public async ValueTask LockAsync(TKey key, CancellationToken cancel /// The number of milliseconds to wait, (-1) to wait indefinitely, or zero to test the state of the wait handle and return immediately. /// Options used to configure how awaits on this task are performed. /// A disposable value of type . + [MethodImpl(MethodImplOptions.AggressiveInlining)] public async ValueTask> LockAsync(TKey key, int millisecondsTimeout, ConfigureAwaitOptions configureAwaitOptions) { var releaser = GetOrAdd(key); @@ -1282,6 +1317,7 @@ public async ValueTask> LockAsync(TKey key, /// A that represents the number of milliseconds to wait, a that represents -1 milliseconds to wait indefinitely, or a that represents 0 milliseconds to test the wait handle and return immediately. /// Options used to configure how awaits on this task are performed. /// A disposable value of type . + [MethodImpl(MethodImplOptions.AggressiveInlining)] public async ValueTask> LockAsync(TKey key, TimeSpan timeout, ConfigureAwaitOptions configureAwaitOptions) { var releaser = GetOrAdd(key); @@ -1296,6 +1332,7 @@ public async ValueTask> LockAsync(TKey key, /// The to observe. /// Options used to configure how awaits on this task are performed. /// A disposable value of type . + [MethodImpl(MethodImplOptions.AggressiveInlining)] public async ValueTask> LockAsync(TKey key, int millisecondsTimeout, CancellationToken cancellationToken, ConfigureAwaitOptions configureAwaitOptions) { var releaser = GetOrAdd(key); @@ -1318,6 +1355,7 @@ public async ValueTask> LockAsync(TKey key, /// The to observe. /// Options used to configure how awaits on this task are performed. /// A disposable value of type . + [MethodImpl(MethodImplOptions.AggressiveInlining)] public async ValueTask> LockAsync(TKey key, TimeSpan timeout, CancellationToken cancellationToken, ConfigureAwaitOptions configureAwaitOptions) { var releaser = GetOrAdd(key); @@ -1339,6 +1377,7 @@ public async ValueTask> LockAsync(TKey key, /// /// The key requests are locked on. /// if the key is in use; otherwise, false. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsInUse(TKey key) { if (!_dictionary.TryGetValue(key, out var result)) @@ -1376,6 +1415,7 @@ public int GetCount(TKey key) /// /// The key requests are locked on. /// The number of requests concurrently locked for a given key. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public int GetRemainingCount(TKey key) { if (_dictionary.TryGetValue(key, out var releaser)) @@ -1390,6 +1430,7 @@ public int GetRemainingCount(TKey key) /// /// The key requests are locked on. /// The number of remaining threads that can enter the lock for a given key. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public int GetCurrentCount(TKey key) { return MaxCount - GetRemainingCount(key); @@ -1398,6 +1439,7 @@ public int GetCurrentCount(TKey key) /// /// Disposes the AsyncKeyedLocker. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Dispose() { _dictionary.Dispose(); diff --git a/README.md b/README.md index 56faab9..18cfba0 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,25 @@ For example, suppose you were processing financial transactions, but while worki The library uses two very different methods for locking, one using an underlying `ConcurrentDictionary` that's cleaned up after use whilst the other using a technique called striped locking. Both have their advantages and disadvantages, and in order to help you choose you are highly recommended to read about it in the [wiki](https://github.com/MarkCiliaVincenti/AsyncKeyedLock/wiki). ## Installation and usage +Using this library is straightforward. Here's a simple example: +```csharp +private static readonly AsyncKeyedLocker _asyncKeyedLocker = new(o => + { + o.PoolSize = 20; // this is NOT a concurrency limit + o.PoolInitialFill = 1; + }); + +... + +using (await _asyncKeyedLocker.LockAsync("test123")) +{ + ... +} +``` + The documentation can be found in our [wiki](https://github.com/MarkCiliaVincenti/AsyncKeyedLock/wiki). +Usage + ## Credits Check out our [list of contributors](https://github.com/MarkCiliaVincenti/AsyncKeyedLock/blob/master/CONTRIBUTORS.md)!