From cba62295a30eb723be769a03c9b2b22ecb25ed7d Mon Sep 17 00:00:00 2001 From: "o.nadymov" Date: Tue, 27 Feb 2024 17:15:24 +0300 Subject: [PATCH] Separate locks for different objects (sync + async) --- src/Spoleto.Common/Locks/AsyncLock.cs | 32 ++++++ src/Spoleto.Common/Locks/ObjectAsyncLocks.cs | 109 +++++++++++++++++++ src/Spoleto.Common/Locks/ObjectLocks.cs | 80 ++++++++++++++ 3 files changed, 221 insertions(+) create mode 100644 src/Spoleto.Common/Locks/AsyncLock.cs create mode 100644 src/Spoleto.Common/Locks/ObjectAsyncLocks.cs create mode 100644 src/Spoleto.Common/Locks/ObjectLocks.cs diff --git a/src/Spoleto.Common/Locks/AsyncLock.cs b/src/Spoleto.Common/Locks/AsyncLock.cs new file mode 100644 index 0000000..7429745 --- /dev/null +++ b/src/Spoleto.Common/Locks/AsyncLock.cs @@ -0,0 +1,32 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Spoleto.Common.Locks +{ + /// + /// The special lock for async body. + /// + public class AsyncLock : IDisposable + { + private readonly SemaphoreSlim _semaphoreSlim = new(1, 1); + + /// + /// Creates the lock. + /// + /// + public async Task LockAsync() + { + await _semaphoreSlim.WaitAsync().ConfigureAwait(false); + return this; + } + + /// + /// Releases the lock. + /// + public void Dispose() + { + _semaphoreSlim.Release(); + } + } +} diff --git a/src/Spoleto.Common/Locks/ObjectAsyncLocks.cs b/src/Spoleto.Common/Locks/ObjectAsyncLocks.cs new file mode 100644 index 0000000..c34e5c7 --- /dev/null +++ b/src/Spoleto.Common/Locks/ObjectAsyncLocks.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Spoleto.Common.Locks +{ + /// + /// The static class for separate async locks for objects. + /// + /// + /// Example: + /// var lockObject = ObjectAsyncLocks.GetObjectLock(lockObjetKey);
+ /// using (await lockObject.LockAsync())
+ /// {
+ /// try
+ /// {
+ /// // code
+ /// }
+ /// finally
+ /// {
+ /// ObjectAsyncLocks.ReleaseObjectLock(lockObjetKey);
+ /// }
+ /// }
+ ///
+ public static class ObjectAsyncLocks + { + /// + /// The class of async locks with deep. + /// + public class AsyncIntLock : IDisposable + { + /// + /// The async lock object. + /// + private readonly AsyncLock _asyncLock; + + /// + /// Default constructor. + /// + public AsyncIntLock() + { + _asyncLock = new AsyncLock(); + } + + /// + /// Locks deep. + /// + public int Deep { get; set; } + + /// + /// Releases the lock. + /// + public void Dispose() => _asyncLock.Dispose(); + + /// + /// Creates the lock. + /// + public async Task LockAsync() + { + await _asyncLock.LockAsync().ConfigureAwait(false); + return this; + } + + /// + /// Return text representation. + /// + /// + public override string ToString() => $"Deep = {Deep}"; + } + + + private static readonly Dictionary ObjectLocksDic = new(); + private static readonly object CacheLock = new(); + + /// + /// Returns lock for object. + /// + public static AsyncIntLock GetObjectLock(string key) + { + lock (CacheLock) + { + if (!ObjectLocksDic.TryGetValue(key, out var cachedItem)) + { + cachedItem = new AsyncIntLock(); + ObjectLocksDic.Add(key, cachedItem); + } + + cachedItem.Deep++; + return cachedItem; + } + } + + /// + /// Release object. + /// + public static void ReleaseObjectLock(string key) + { + lock (CacheLock) + { + if (ObjectLocksDic.TryGetValue(key, out var cachedItem)) + { + cachedItem.Deep--; + if (cachedItem.Deep == 0) + ObjectLocksDic.Remove(key); + } + } + } + } +} \ No newline at end of file diff --git a/src/Spoleto.Common/Locks/ObjectLocks.cs b/src/Spoleto.Common/Locks/ObjectLocks.cs new file mode 100644 index 0000000..c8b67df --- /dev/null +++ b/src/Spoleto.Common/Locks/ObjectLocks.cs @@ -0,0 +1,80 @@ +using System.Collections.Generic; + +namespace Spoleto.Common.Locks +{ + /// + /// The static class for separate locks for objects. + /// + /// + /// Example: + /// object lockObj = ObjectLocks.GetObjectLock(lockObjetKey);
+ /// lock (lockObj) + /// {
+ /// try
+ /// {
+ /// // code
+ /// }
+ /// finally
+ /// {
+ /// ObjectLocks.ReleaseObjectLock(lockObjetKey);
+ /// }
+ /// }
+ ///
+ public static class ObjectLocks + { + /// + /// The class of locks with deep. + /// + private class IntLock + { + /// + /// Locks deep. + /// + public int Deep { get; set; } + + /// + /// Return text representation. + /// + /// + public override string ToString() => $"Deep = {Deep}"; + } + + + private static readonly Dictionary ObjectLocksDic = new Dictionary(); + private static readonly object CacheLock = new object(); + + /// + /// Returns lock for object. + /// + public static object GetObjectLock(string key) + { + lock (CacheLock) + { + if (!ObjectLocksDic.TryGetValue(key, out var cachedItem)) + { + cachedItem = new IntLock(); + ObjectLocksDic.Add(key, cachedItem); + } + + cachedItem.Deep++; + return cachedItem; + } + } + + /// + /// Release object. + /// + public static void ReleaseObjectLock(string key) + { + lock (CacheLock) + { + if (ObjectLocksDic.TryGetValue(key, out var cachedItem)) + { + cachedItem.Deep--; + if (cachedItem.Deep == 0) + ObjectLocksDic.Remove(key); + } + } + } + } +} \ No newline at end of file