基于密钥的异步锁定:间歇性文件访问错误的根源
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中文网其他相关文章!