问题描述
public ConcurrentDictionary<Guid,DeviceItem> deviceStatesCache = new ConcurrentDictionary<Guid,DeviceItem>();
private readonly object deviceStatesCacheLock = new object();
public void StoreDeviceStateInCache(Guid guid,DeviceItem deviceState)
{
bool added,removed,updated = false;
lock (deviceStatesCacheLock)
{
added = deviceStatesCache.TryAdd(guid,deviceState);
if (!added) {
removed = deviceStatesCache.TryRemove(guid,out var _);
if (removed)
{
updated = deviceStatesCache.TryAdd(guid,deviceState);
}
}
}
if (!updated)
throw new Exception("WTF: cannot update deviceStatesCache!");
}
private QueryDevicesResponse createqueryDevicesResponse()
{
var deviceItems = new List<DeviceItem>();
lock (deviceStatesCacheLock)
{
foreach (var item in deviceStatesCache)
{
deviceItems.Add(item.Value);
}
}
var response = new QueryDevicesResponse()
{
eventData = new QueryDevicesResponse.EventData()
{
devices = deviceItems
}
};
// response.eventSourceGuid = Guid.Empty.ToString();
return response;
}
(编辑)
我的lock
应该可以在以下情况下工作:
-
当另一个线程开始以
createqueryDevicesResponse()
方法读取所有项目时。
我可以简化一下 StoreDeviceStateInCache()
方法吗?
我知道AddOrUpdate方法存在,但是由于必须定义其他方法,而且看起来比我的代码可读性差,因此我找不到它。
看起来Michael Liu suggestion和deviceStatesCache[guid] = deviceState;
就足够了
我可以稍微简化createqueryDevicesResponse()
方法并“原子地”阅读完整的词典吗?
(WTF当然代表“多么严重的失败”)
解决方法
如果我正确地理解了您的代码,那么如果键不存在,则要添加一个字典条目,如果键不存在,则要替换字典条目。
您可以使用ConcurrentDictionary的索引器来完成此操作,而无需任何其他锁定:
deviceStatesCache[guid] = deviceState;
,
您也可以使用ConcurrentDictionary类型的AddOrUpdate方法来实现此目的。像下面这样
deviceStatesCache.AddOrUpdate(guid,deviceState,(key,value)=> deviceState);