Home >Backend Development >C++ >How to Correctly Implement Asynchronous Locking Using SemaphoreSlim to Prevent Intermittent File Access Errors?

How to Correctly Implement Asynchronous Locking Using SemaphoreSlim to Prevent Intermittent File Access Errors?

Barbara Streisand
Barbara StreisandOriginal
2025-01-15 09:49:50887browse

How to Correctly Implement Asynchronous Locking Using SemaphoreSlim to Prevent Intermittent File Access Errors?

Addressing Intermittent File Access Errors with Asynchronous Locking

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!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn