Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在C#中包含对象列表的字典上使用锁?_C#_Multithreading - Fatal编程技术网

如何在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);

        ....
    }
}