基于键的异步锁定
本文讨论了 ImageProcessor 库中遇到的一个问题:在添加缓存期间发生文件访问间歇性错误。错误消息表明相关文件正在被另一个进程使用。为了解决这个问题,实现了一种基于键的异步锁定机制。然而,后来发现该实现存在疏忽。
问题所在
最初的锁定类 AsyncDuplicateLock
试图通过使用键从并发字典中检索信号量来执行异步锁定。但是,它在释放信号量之前错误地将其从字典中删除。这意味着信号量在仍在使用时被删除,导致观察到的文件访问错误。
解决方案
为了解决这个问题,修改了锁定类。更新后的版本没有使用基于并发字典的无锁方法,而是使用更传统的方法,结合引用计数和单个锁。这确保了只有在信号量不再使用时才释放它们。
以下是修改后的锁定类代码:
<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中文网其他相关文章!