gpt4 book ai didi

c# - 基于key的异步锁

转载 作者:可可西里 更新时间:2023-11-01 08:01:52 26 4
gpt4 key购买 nike

我正在尝试找出我的 ImageProcessor 库中出现的问题 here将项目添加到缓存时出现间歇性文件访问错误。

System.IO.IOException: The process cannot access the file 'D:\home\site\wwwroot\app_data\cache\0\6\5\f\2\7\065f27fc2c8e843443d210a1e84d1ea28bbab6c4.webp' because it is being used by another process.

我编写了一个类,旨在根据哈希 url 生成的 key 执行异步锁定,但似乎我在实现中遗漏了一些东西。

我的加锁类

public sealed class AsyncDuplicateLock
{
/// <summary>
/// The collection of semaphore slims.
/// </summary>
private static readonly ConcurrentDictionary<object, SemaphoreSlim> SemaphoreSlims
= new ConcurrentDictionary<object, SemaphoreSlim>();

/// <summary>
/// Locks against the given key.
/// </summary>
/// <param name="key">
/// The key that identifies the current object.
/// </param>
/// <returns>
/// The disposable <see cref="Task"/>.
/// </returns>
public IDisposable Lock(object key)
{
DisposableScope releaser = new DisposableScope(
key,
s =>
{
SemaphoreSlim locker;
if (SemaphoreSlims.TryRemove(s, out locker))
{
locker.Release();
locker.Dispose();
}
});

SemaphoreSlim semaphore = SemaphoreSlims.GetOrAdd(key, new SemaphoreSlim(1, 1));
semaphore.Wait();
return releaser;
}

/// <summary>
/// Asynchronously locks against the given key.
/// </summary>
/// <param name="key">
/// The key that identifies the current object.
/// </param>
/// <returns>
/// The disposable <see cref="Task"/>.
/// </returns>
public Task<IDisposable> LockAsync(object key)
{
DisposableScope releaser = new DisposableScope(
key,
s =>
{
SemaphoreSlim locker;
if (SemaphoreSlims.TryRemove(s, out locker))
{
locker.Release();
locker.Dispose();
}
});

Task<IDisposable> releaserTask = Task.FromResult(releaser as IDisposable);
SemaphoreSlim semaphore = SemaphoreSlims.GetOrAdd(key, new SemaphoreSlim(1, 1));

Task waitTask = semaphore.WaitAsync();

return waitTask.IsCompleted
? releaserTask
: waitTask.ContinueWith(
(_, r) => (IDisposable)r,
releaser,
CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Default);
}

/// <summary>
/// The disposable scope.
/// </summary>
private sealed class DisposableScope : IDisposable
{
/// <summary>
/// The key
/// </summary>
private readonly object key;

/// <summary>
/// The close scope action.
/// </summary>
private readonly Action<object> closeScopeAction;

/// <summary>
/// Initializes a new instance of the <see cref="DisposableScope"/> class.
/// </summary>
/// <param name="key">
/// The key.
/// </param>
/// <param name="closeScopeAction">
/// The close scope action.
/// </param>
public DisposableScope(object key, Action<object> closeScopeAction)
{
this.key = key;
this.closeScopeAction = closeScopeAction;
}

/// <summary>
/// Disposes the scope.
/// </summary>
public void Dispose()
{
this.closeScopeAction(this.key);
}
}
}

用法 - 在 HttpModule 中

private readonly AsyncDuplicateLock locker = new AsyncDuplicateLock();

using (await this.locker.LockAsync(cachedPath))
{
// Process and save a cached image.
}

谁能发现我哪里出错了?我担心我误解了一些基本的东西。

该库的完整源代码存储在 Github 上 here

最佳答案

作为other answerer noted ,原始代码在释放信号量之前从 ConcurrentDictionary 中删除 SemaphoreSlim。所以,你有太多的信号量流失 - 它们在它们仍然可以使用时被从字典中删除(不是获取,但已经从字典中检索)。

这种“映射锁”的问题是很难知道什么时候不再需要信号量。一种选择是根本不处理信号量;这是简单的解决方案,但在您的场景中可能 Not Acceptable 。另一种选择 - 如果信号量实际上与对象实例相关而不是值(如字符串) - 是使用 ephemerons 附加它们;但是,我相信这个选项在您的场景中也是 Not Acceptable 。

所以,我们以艰难的方式做到这一点。 :)

有几种不同的方法可行。我认为从引用计数的角度来处理它是有意义的(引用计数字典中的每个信号量)。此外,我们希望使递减计数和删除操作成为原子操作,所以我只使用一个 lock(使并发字典变得多余):

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();
}
}
}

关于c# - 基于key的异步锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31138179/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com