如何在C#中包含对象列表的字典上使用锁?
我有以下课程:如何在C#中包含对象列表的字典上使用锁?,c#,multithreading,C#,Multithreading,我有以下课程: public static class HotspotsCache { private static Dictionary<short, List<HotSpot>> _companyHotspots = new Dictionary<int, List<HotSpot>>(); private static object Lock = new object(); public static List<HotS
public static class HotspotsCache
{
private static Dictionary<short, List<HotSpot>> _companyHotspots = new Dictionary<int, List<HotSpot>>();
private static object Lock = new object();
public static List<HotSpot> GetCompanyHotspots(short companyId)
{
lock (Lock)
{
if (!_companyHotspots.ContainsKey(companyId))
{
RefreshCompanyHotspotCache(companyId);
}
return _companyHotspots[companyId];
}
}
private static void RefreshCompanyHotspotCache(short companyId)
{
....
hotspots = ServiceProvider.Instance.GetService<HotspotsService>().GetHotSpots(..);
_companyHotspots.Add(companyId, hotspots);
....
}
但这并没有解决任何问题。与一家公司打交道的线程仍在等待为其他公司刷新缓存的线程。您查看过ReaderWriterLockSlim吗?这应该能够让您获得更细粒度的锁定,您只需要在需要时进行写回 你可能需要注意的另一件事是虚假分享。我不知道锁是如何具体实现的,但如果锁定数组中的对象,它们在内存中肯定会彼此靠近,可能会将它们放在同一个缓存线上,因此锁的行为可能不会像您预期的那样 另一个想法是,如果将最后一个代码段更改为
object l = companyLocks[companyId];
lock(l){
}
可能是lock语句在这里比预期的要多
GJ如果你只锁定一个线程,让它更新,而其他人都使用旧的列表,怎么样
private static Dictionary<short, List<HotSpot>> _companyHotspots = new Dictionary<short, List<HotSpot>>();
private static Dictionary<short, List<HotSpot>> _companyHotspotsOld = new Dictionary<short, List<HotSpot>>();
private static bool _hotspotsUpdating = false;
private static object Lock = new object();
public static List<HotSpot> GetCompanyHotspots(short companyId)
{
if (!_hotspotsUpdating)
{
if (!_companyHotspots.ContainsKey(companyId))
{
lock (Lock)
{
_hotspotsUpdating = true;
_companyHotspotsOld = _companyHotspots;
RefreshCompanyHotspotCache(companyId);
_hotspotsUpdating = false;
return _companyHotspots[companyId];
}
}
else
{
return _companyHotspots[companyId];
}
}
else
{
return _companyHotspotsOld[companyId];
}
}
private static Dictionary\u companyHotspots=new Dictionary();
私有静态字典_companyhotspotsaled=new Dictionary();
私有静态bool_hostpotsupdating=false;
私有静态对象锁=新对象();
公共静态列表GetCompanyHotspots(短公司ID)
{
如果(!\u hotspotsUpdating)
{
如果(!_companyHotspots.ContainsKey(companyId))
{
锁(锁)
{
_hotspotsUpdating=真;
_companyHotspotsOld=_companyHotspots;
刷新CompanyHotSpotCache(companyId);
_hostpotsupdating=false;
return_companyHotspots[公司ID];
}
}
其他的
{
return_companyHotspots[公司ID];
}
}
其他的
{
return_companyhotspottsell[companyId];
}
}
使用Snowbear也提到的双重检查锁定机制-这将在不需要时防止代码锁定
根据您关于每个客户机一个单独锁的想法,我在过去使用过这种机制,尽管我使用了锁字典。我创建了一个实用程序类,用于从密钥获取锁对象:
/// <summary>
/// Provides a mechanism to lock based on a data item being retrieved
/// </summary>
/// <typeparam name="T">Type of the data being used as a key</typeparam>
public class LockProvider<T>
{
private object _syncRoot = new object();
private Dictionary<T, object> _lstLocks = new Dictionary<T, object>();
/// <summary>
/// Gets an object suitable for locking the specified data item
/// </summary>
/// <param name="key">The data key</param>
/// <returns></returns>
public object GetLock(T key)
{
if (!_lstLocks.ContainsKey(key))
{
lock (_syncRoot)
{
if (!_lstLocks.ContainsKey(key))
_lstLocks.Add(key, new object());
}
}
return _lstLocks[key];
}
}
//
///提供基于正在检索的数据项进行锁定的机制
///
///用作键的数据的类型
公共类锁提供者
{
私有对象_syncRoot=新对象();
私有字典_lstLocks=新字典();
///
///获取适合于锁定指定数据项的对象
///
///数据键
///
公共对象GetLock(T键)
{
如果(!lstLocks.ContainsKey(钥匙))
{
锁定(\u syncRoot)
{
如果(!lstLocks.ContainsKey(钥匙))
_添加(键,新对象());
}
}
返回_lstLocks[键];
}
}
因此,只需按照以下方式使用它
private static LockProvider<short> _clientLocks = new LockProvider<short>();
private static Dictionary<short, List<HotSpot>> _companyHotspots = new Dictionary<short, List<HotSpot>>();
public static List<HotSpot> GetCompanyHotspots(short companyId)
{
if (!_companyHotspots.ContainsKey(companyId))
{
lock (_clientLocks.GetLock(companyId))
{
if (!_companyHotspots.ContainsKey(companyId))
{
// Add item to _companyHotspots here...
}
}
return _companyHotspots[companyId];
}
private static LockProvider\u clientLocks=new LockProvider();
私有静态字典_companyHotspots=新字典();
公共静态列表GetCompanyHotspots(短公司ID)
{
如果(!_companyHotspots.ContainsKey(companyId))
{
锁(_clientLocks.GetLock(companyId))
{
如果(!_companyHotspots.ContainsKey(companyId))
{
//将项目添加到_companyHotspots此处。。。
}
}
return_companyHotspots[公司ID];
}
如果您能够使用.NET 4,那么答案很简单——改用a,让它为您处理并发细节:
public static class HotSpotsCache
{
private static readonly ConcurrentDictionary<short, List<HotSpot>>
_hotSpotsMap = new ConcurrentDictionary<short, List<HotSpot>>();
public static List<HotSpot> GetCompanyHotSpots(short companyId)
{
return _hotSpotsMap.GetOrAdd(companyId, id => LoadHotSpots(id));
}
private static List<HotSpot> LoadHotSpots(short companyId)
{
return ServiceProvider.Instance
.GetService<HotSpotsService>()
.GetHotSpots(/* ... */);
}
}
公共静态类热点缓存
{
私有静态只读ConcurrentDictionary
_hotSpotsMap=新的ConcurrentDictionary();
公共静态列表GetCompanyHotSpots(短公司ID)
{
return _hostspotsmap.GetOrAdd(companyId,id=>loadhostspots(id));
}
私有静态列表加载热点(短公司ID)
{
返回ServiceProvider.Instance
.GetService()
.GetHotSpots(/*…*/);
}
}
如果您不能使用.NET 4,那么您使用多个细粒度锁的想法是很好的:
public static class HotSpotsCache
{
private static readonly Dictionary<short, List<HotSpot>>
_hotSpotsMap = new Dictionary<short, List<HotSpot>();
private static readonly object _bigLock = new object();
private static readonly Dictionary<short, object>
_miniLocks = new Dictionary<short, object>();
public static List<HotSpot> GetCompanyHotSpots(short companyId)
{
List<HotSpot> hotSpots;
object miniLock;
lock (_bigLock)
{
if (_hotSpotsMap.TryGetValue(companyId, out hotSpots))
return hotSpots;
if (!_miniLocks.TryGetValue(companyId, out miniLock))
{
miniLock = new object();
_miniLocks.Add(companyId, miniLock);
}
}
lock (miniLock)
{
if (!_hotSpotsMap.TryGetValue(companyId, out hotSpots))
{
hotSpots = LoadHotSpots(companyId);
lock (_bigLock)
{
_hotSpotsMap.Add(companyId, hotSpots);
_miniLocks.Remove(companyId);
}
}
return hotSpots;
}
}
private static List<HotSpot> LoadHotSpots(short companyId)
{
return ServiceProvider.Instance
.GetService<HotSpotsService>()
.GetHotSpots(/* ... */);
}
}
公共静态类热点缓存
{
专用静态只读字典
_hotSpotsMap=newdictionarynewidea,在创建列表时只锁定列表
如果可以保证每家公司至少有一个热点,请执行以下操作:
public static class HotspotsCache
{
private static Dictionary<short, List<HotSpot>> _companyHotspots = new Dictionary<int, List<HotSpot>>();
static HotspotsCache()
{
foreach(short companyId in allCompanies)
{
companyHotspots.Add(companyId, new List<HotSpot>());
}
}
public static List<HotSpot> GetCompanyHotspots(short companyId)
{
List<HotSpots> result = _companyHotspots[companyId];
if(result.Count == 0)
{
lock(result)
{
if(result.Count == 0)
{
RefreshCompanyHotspotCache(companyId, result);
}
}
}
return result;
}
private static void RefreshCompanyHotspotCache(short companyId, List<HotSpot> resultList)
{
....
hotspots = ServiceProvider.Instance.GetService<HotspotsService>().GetHotSpots(..);
resultList.AddRange(hotspots);
....
}
}
公共静态类热点缓存
{
私有静态字典_companyHotspots=新字典();
静态热点
{
foreach(所有公司中的短公司ID)
{
添加(companyId,newlist());
}
}
公共静态列表GetCompanyHotspots(短公司ID)
{
列表结果=_companyHotspots[companyId];
如果(result.Count==0)
{
锁定(结果)
{
如果(result.Count==0)
{
刷新CompanyHotSpotCache(companyId,结果);
}
}
}
返回结果;
}
私有静态void RefreshCompanyHotspotCache(短公司ID,列表结果列表)
{
....
热点=ServiceProvider.Instance.GetService().GetHotSpots(..);
结果列表.AddRange(热点);
....
}
}
由于字典在初始创建后正在修改,因此无需对其进行任何锁定。我们只需在填充单个列表时锁定它们,读取操作无需锁定(包括初始计数==0).正如您在这里列出的代码一样,对GetCompanyHotspots
的所有调用都会导致显式刷新。您是否读取过缓存数据?这就是我读取缓存数据的地方:return _companyHotspots[companyId];我不会每次都显式刷新。您有“if dictionary.containsKey(id){refresh(id);}return dictionary[id];”。如果containsKey返回false,则不会刷新它,而是返回状态
public static class HotspotsCache
{
private static Dictionary<short, List<HotSpot>> _companyHotspots = new Dictionary<int, List<HotSpot>>();
static HotspotsCache()
{
foreach(short companyId in allCompanies)
{
companyHotspots.Add(companyId, new List<HotSpot>());
}
}
public static List<HotSpot> GetCompanyHotspots(short companyId)
{
List<HotSpots> result = _companyHotspots[companyId];
if(result.Count == 0)
{
lock(result)
{
if(result.Count == 0)
{
RefreshCompanyHotspotCache(companyId, result);
}
}
}
return result;
}
private static void RefreshCompanyHotspotCache(short companyId, List<HotSpot> resultList)
{
....
hotspots = ServiceProvider.Instance.GetService<HotspotsService>().GetHotSpots(..);
resultList.AddRange(hotspots);
....
}
}