如何正确锁定并发多个请求
How to properly lock with concurrent multiple requests
我的系统中有一个自动刷新缓存,由于竞争条件,运行 出现了一些问题。
在启动期间,作为并发字典的 _internalCache
为空。
这是几年前实现的,作为我们系统中使用的通用自动刷新缓存。
导致大部分问题的刷新操作从数据库中刷新了几千行。
public bool TryGet(TKey key, out TValue value)
{
if (_internalCache.TryGetValue(key, out value))
{
return true;
}
lock (_internalCache.SyncRoot)
{
this._refreshCacheAction(this._internalCache);
return _internalCache.TryGetValue(key, out value);
}
}
如果多个请求同时进入(这种情况发生的频率比我希望的要高),那么我们会多次刷新缓存。
编辑:
经过评论的进一步讨论,看起来这个缓存被严重破坏了。我们的一些客户遇到超时问题,我需要快速修补程序。
如何防止多次刷新缓存?
(欢迎 Jenky hack)
确保包含 TryGet
的 class 是跨所有调用的单例实例,因为它在应用程序生命周期内只应创建一次。私有实例构造函数与引用单个实例的 class 上的静态 属性 相结合,在 class 的静态构造函数中构造就足够了:
public class ASingletonClass
{
static ASingletonClass()
{
Instance = new ASingletonClass();
}
private ASingletonClass()
{
}
public static ASingletonClass Instance { get; private set; }
}
此外,将 SyncRoot 调用替换为 class 中设置为 new object()
的新对象字段。 ConcurrentDictionary 等较新的集合不支持 SyncRoot。
总的来说,设计看起来有缺陷。甚至可以使用像 ConcurrentDictionary 或 MemoryCache 这样的标准组件。
但是,一种可能的修补程序是再次检查锁内部缓存中的值。这应该会减少执行刷新操作的次数。
public bool TryGet(TKey key, out TValue value)
{
if (_internalCache.TryGetValue(key, out value))
{
return true;
}
lock (_internalCache.SyncRoot)
{
// cache has already been refreshed
if (_internalCache.TryGetValue(key, out value))
{
return true;
}
// refresh cache
this._refreshCacheAction(this._internalCache);
return _internalCache.TryGetValue(key, out value);
}
}
我的系统中有一个自动刷新缓存,由于竞争条件,运行 出现了一些问题。
在启动期间,作为并发字典的 _internalCache
为空。
这是几年前实现的,作为我们系统中使用的通用自动刷新缓存。
导致大部分问题的刷新操作从数据库中刷新了几千行。
public bool TryGet(TKey key, out TValue value)
{
if (_internalCache.TryGetValue(key, out value))
{
return true;
}
lock (_internalCache.SyncRoot)
{
this._refreshCacheAction(this._internalCache);
return _internalCache.TryGetValue(key, out value);
}
}
如果多个请求同时进入(这种情况发生的频率比我希望的要高),那么我们会多次刷新缓存。
编辑: 经过评论的进一步讨论,看起来这个缓存被严重破坏了。我们的一些客户遇到超时问题,我需要快速修补程序。
如何防止多次刷新缓存? (欢迎 Jenky hack)
确保包含 TryGet
的 class 是跨所有调用的单例实例,因为它在应用程序生命周期内只应创建一次。私有实例构造函数与引用单个实例的 class 上的静态 属性 相结合,在 class 的静态构造函数中构造就足够了:
public class ASingletonClass
{
static ASingletonClass()
{
Instance = new ASingletonClass();
}
private ASingletonClass()
{
}
public static ASingletonClass Instance { get; private set; }
}
此外,将 SyncRoot 调用替换为 class 中设置为 new object()
的新对象字段。 ConcurrentDictionary 等较新的集合不支持 SyncRoot。
总的来说,设计看起来有缺陷。甚至可以使用像 ConcurrentDictionary 或 MemoryCache 这样的标准组件。
但是,一种可能的修补程序是再次检查锁内部缓存中的值。这应该会减少执行刷新操作的次数。
public bool TryGet(TKey key, out TValue value)
{
if (_internalCache.TryGetValue(key, out value))
{
return true;
}
lock (_internalCache.SyncRoot)
{
// cache has already been refreshed
if (_internalCache.TryGetValue(key, out value))
{
return true;
}
// refresh cache
this._refreshCacheAction(this._internalCache);
return _internalCache.TryGetValue(key, out value);
}
}