基於金鑰的非同步鎖定:間歇性文件存取錯誤的根源
ImageProcessor 函式庫的快取機制採用基於金鑰的非同步鎖定,會出現間歇性檔案存取錯誤。 這源自於 AsyncDuplicateLock
類別中的設計缺陷。
缺陷:訊號量過早釋放
原始的 AsyncDuplicateLock
程式碼在釋放訊號量之前會提前從 SemaphoreSlim
中刪除 ConcurrentDictionary
實例。這會導致信號量過度流失和潛在錯誤,因為信號量在刪除後可能會被存取。
解:穩健的引用計數方法
一個優秀的解決方案利用引用計數。 字典中的每個信號量都維護一個引用計數。 單一鎖定保證了遞減計數和刪除信號量的原子性,從而消除了對 ConcurrentDictionary
.
<code class="language-csharp">public sealed class AsyncDuplicateLock { private sealed class RefCounted<T> { public RefCounted(T value) { RefCount = 1; Value = value; } public int RefCount { get; set; } public T Value { get; private set; } } private static readonly Dictionary<object, RefCounted<SemaphoreSlim>> SemaphoreSlims = new Dictionary<object, RefCounted<SemaphoreSlim>>(); private SemaphoreSlim GetOrCreate(object key) { RefCounted<SemaphoreSlim> item; lock (SemaphoreSlims) { if (SemaphoreSlims.TryGetValue(key, out item)) { item.RefCount++; } else { item = new RefCounted<SemaphoreSlim>(new SemaphoreSlim(1, 1)); SemaphoreSlims[key] = item; } } return item.Value; } public IDisposable Lock(object key) { GetOrCreate(key).Wait(); return new Releaser { Key = key }; } public async Task<IDisposable> LockAsync(object key) { await GetOrCreate(key).WaitAsync().ConfigureAwait(false); return new Releaser { Key = key }; } private sealed class Releaser : IDisposable { public object Key { get; set; } public void Dispose() { RefCounted<SemaphoreSlim> item; lock (SemaphoreSlims) { item = SemaphoreSlims[Key]; item.RefCount--; if (item.RefCount == 0) SemaphoreSlims.Remove(Key); } item.Value.Release(); } } }</code>
這種修改後的方法保證信號量僅在不再需要時才被釋放,有效防止快取操作期間間歇性的檔案存取錯誤。
以上是為什麼基於鍵的非同步鎖定會導致間歇性文件存取錯誤,引用計數方法如何改進它?的詳細內容。更多資訊請關注PHP中文網其他相關文章!