Home >Backend Development >C++ >How to Correctly Implement Asynchronous Locking Using SemaphoreSlim to Prevent Intermittent File Access Errors?
The ImageProcessor library recently encountered intermittent file access errors during cache updates, stemming from flawed asynchronous locking. This article analyzes the original faulty implementation and provides a robust solution.
The original approach used hashed URLs as keys for asynchronous locks, storing SemaphoreSlim
instances in a ConcurrentDictionary
to ensure single-key locking. The critical flaw lay in synchronously removing the SemaphoreSlim
from the dictionary before releasing the semaphore. This race condition led to file access conflicts.
The following code snippet illustrates the problematic key/SemaphoreSlim
referencing:
<code class="language-csharp">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; }</code>
Below is the corrected AsyncDuplicateLock
class, resolving the concurrency issue:
<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>
This revised implementation ensures the semaphore is released before removal from the dictionary, eliminating the race condition and resolving the intermittent file access errors.
The above is the detailed content of How to Correctly Implement Asynchronous Locking Using SemaphoreSlim to Prevent Intermittent File Access Errors?. For more information, please follow other related articles on the PHP Chinese website!