diff --git a/FolderSyncNet.csproj b/FolderSyncNet.csproj
index 45125ec..000ddd9 100644
--- a/FolderSyncNet.csproj
+++ b/FolderSyncNet.csproj
@@ -77,6 +77,9 @@
     
       packages\AsyncEnumerator.4.0.2\lib\net461\AsyncEnumerable.dll
     
+    
+      packages\AsyncKeyedLock.7.1.3\lib\netstandard2.0\AsyncKeyedLock.dll
+    
     
       packages\Microsoft.Bcl.AsyncInterfaces.7.0.0\lib\net462\Microsoft.Bcl.AsyncInterfaces.dll
     
@@ -396,4 +399,4 @@
     
   
   
-
\ No newline at end of file
+
diff --git a/InitialScan.cs b/InitialScan.cs
index 24f6560..8394aef 100644
--- a/InitialScan.cs
+++ b/InitialScan.cs
@@ -242,7 +242,7 @@ private static IAsyncEnumerable ProcessSubDirs(FolderSyncNetSource.Dir
                 var historyFileInfosDict = new Dictionary();
                 //var historyFileInfosTask = Task.CompletedTask;
 
-                AsyncLockQueueDictionary.LockDictReleaser destOrHistoryDirCacheLock = null;
+                IDisposable destOrHistoryDirCacheLock = null;
                 //AsyncLockQueueDictionary.LockDictReleaser historyDirCacheLock = null;
 
                 FileInfo[] fileInfos = null;
diff --git a/Synchronisation.cs b/Synchronisation.cs
index 864c39a..831594b 100644
--- a/Synchronisation.cs
+++ b/Synchronisation.cs
@@ -7,141 +7,39 @@
 //
 
 #define ASYNC
+using AsyncKeyedLock;
 using System;
 using System.Collections.Generic;
-using System.Diagnostics;
+using System.Runtime.CompilerServices;
 using System.Threading;
 using System.Threading.Tasks;
-using Nito.AsyncEx;
 
 namespace FolderSync
 {
     public class AsyncLockQueueDictionary
         where KeyT : IComparable, IEquatable
     {
-        private static readonly bool IsStringDictionary = typeof(KeyT) == typeof(string);
-
-        private readonly object DictionaryAccessMutex = new object();
-        //private readonly SemaphoreSlim DictionaryAccessMutex = new SemaphoreSlim(1, 1);
-        private readonly Dictionary LockQueueDictionary = new Dictionary();
-
-        public sealed class AsyncLockWithWaiterCount
-        {
-            public readonly AsyncLock LockEntry;
-#pragma warning disable S1104   //Warning	S1104	Make this field 'private' and encapsulate it in a 'public' property.
-            public int WaiterCount;
-#pragma warning restore S1104
-
-            public AsyncLockWithWaiterCount()
-            {
-                this.LockEntry = new AsyncLock();
-                this.WaiterCount = 1;
-            }
-        }
+        private readonly AsyncKeyedLocker LockQueueDictionary = new AsyncKeyedLocker();
 
-        public sealed class LockDictReleaser : IDisposable  //TODO: implement IAsyncDisposable in .NET 5.0
-        {
-            private readonly KeyT Name;
-            private readonly AsyncLockWithWaiterCount LockEntry;
-#if !NOASYNC
-            private readonly IDisposable LockHandle;
-#endif
-            private readonly AsyncLockQueueDictionary AsyncLockQueueDictionary;
-
-#if NOASYNC
-            internal LockDictReleaser(KeyT name, AsyncLockWithWaiterCount lockEntry, AsyncLockQueueDictionary asyncLockQueueDictionary)
-#else 
-            internal LockDictReleaser(KeyT name, AsyncLockWithWaiterCount lockEntry, IDisposable lockHandle, AsyncLockQueueDictionary asyncLockQueueDictionary)
-#endif
-            {
-                this.Name = name;
-                this.LockEntry = lockEntry;
-#if !NOASYNC
-                this.LockHandle = lockHandle;
-#endif
-                this.AsyncLockQueueDictionary = asyncLockQueueDictionary;
-            }
-
-            public void Dispose()
-            {
-#if NOASYNC
-                this.AsyncLockQueueDictionary.ReleaseLock(this.Name, this.LockEntry);
-#else
-                this.AsyncLockQueueDictionary.ReleaseLock(this.Name, this.LockEntry, this.LockHandle);
-#endif
-            }
-        }
+        private static readonly bool IsStringDictionary = typeof(KeyT) == typeof(string);
 
-#if NOASYNC
-        private void ReleaseLock(KeyT name, AsyncLockWithWaiterCount lockEntry)
-#else
-        private void ReleaseLock(KeyT name, AsyncLockWithWaiterCount lockEntry, IDisposable lockHandle)
-#endif
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public async ValueTask LockAsync(KeyT name)
         {
-#if NOASYNC
-            Monitor.Exit(lockEntry.LockEntry);
-#else
-            lockHandle.Dispose();
-#endif
-
-            lock (DictionaryAccessMutex)
-            //DictionaryAccessMutex.Wait();
-            //try
-            {
-                lockEntry.WaiterCount--;
-                Debug.Assert(lockEntry.WaiterCount >= 0);
-
-                if (lockEntry.WaiterCount == 0)   //NB!
-                {
-                    LockQueueDictionary.Remove(name);
-                }
-            }
-            //finally
-            //{
-            //    DictionaryAccessMutex.Release();
-            //}
+            return await LockQueueDictionary.LockAsync(name).ConfigureAwait(false);
         }
 
-        public async Task LockAsync(KeyT name, CancellationToken cancellationToken = default(CancellationToken))
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public async ValueTask LockAsync(KeyT name, CancellationToken cancellationToken)
         {
-            AsyncLockWithWaiterCount lockEntry;
-#pragma warning disable PH_S023     //Message	PH_S023	Using the blocking synchronization mechanics of a monitor inside an async method is discouraged; use SemaphoreSlim instead.
-            lock (DictionaryAccessMutex)
-#pragma warning restore PH_S023
-            //await DictionaryAccessMutex.WaitAsync(cancellationToken);
-            //try
-            {
-                if (!LockQueueDictionary.TryGetValue(name, out lockEntry))
-                {
-                    lockEntry = new AsyncLockWithWaiterCount();
-                    LockQueueDictionary.Add(name, lockEntry);
-                }
-                else
-                {
-                    lockEntry.WaiterCount++;  //NB! must be done inside the lock and BEFORE waiting for the lock
-                }
-            }
-            //finally
-            //{
-            //    DictionaryAccessMutex.Release();
-            //}
-
-#if NOASYNC
-#pragma warning disable PH_P006  //warning PH_P006 Favor the use of the lock-statement instead of the use of Monitor.Enter when no timeouts are needed.
-            Monitor.Enter(lockEntry.LockEntry);
-#pragma warning restore PH_P006
-            return new LockDictReleaser(name, lockEntry, this);
-#else
-            var lockHandle = await lockEntry.LockEntry.LockAsync(cancellationToken);
-            return new LockDictReleaser(name, lockEntry, lockHandle, this);
-#endif
+            return await LockQueueDictionary.LockAsync(name, cancellationToken).ConfigureAwait(false);
         }
 
         public sealed class MultiLockDictReleaser : IDisposable  //TODO: implement IAsyncDisposable in .NET 5.0
         {
-            private readonly LockDictReleaser[] Releasers;
+            private readonly IDisposable[] Releasers;
 
-            public MultiLockDictReleaser(params LockDictReleaser[] releasers)
+            public MultiLockDictReleaser(params IDisposable[] releasers)
             {
                 this.Releasers = releasers;
             }
@@ -156,6 +54,7 @@ public void Dispose()
             }
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public async Task LockAsync(KeyT name1, KeyT name2, CancellationToken cancellationToken = default(CancellationToken))
         {
             var names = new List()
diff --git a/packages.config b/packages.config
index 9f9985a..47dfd9e 100644
--- a/packages.config
+++ b/packages.config
@@ -4,6 +4,7 @@
   
   
   
+