锁定静态查找字典的性能

时间:2014-06-17 03:55:03

标签: c# multithreading dictionary locking

我有一个静态字典,我想用它作为ASP.NET应用程序的内部缓存。读取次数将大大超过写入次数,并且我希望确保以线程安全的方式执行此操作,而不会因无关锁而不必要地损害性能。

我有两个实现 - 第一个使用简单的锁对象和lock键工作,第二个使用ReadWriteLockSlim

标准锁定

public class ConcurrentCache
{
    private static readonly object LocationsLock = new object();

    private static bool _locationsLoaded = false;

    private static readonly ConcurrentDictionary<long, Location> Locations = 
        new ConcurrentDictionary<long, Location>();

    public Location Get(long id)
    {
        EnsureLocationsDictionaryIsPopulated();
        return Locations[id];
    }

    private void EnsureLocationsDictionaryIsPopulated()
    {
        if (Locations.Count > 0 || _locationsLoaded) return;

        // Still locking, even though I'm using a ConcurrentDictionary,
        // so that all locations are loaded only once and I don't have
        // to worry about locking the reads.
        lock (LocationsLock)
        {
            if (Locations.Count > 0 || _locationsLoaded) return;
            PopulateLocationsDictionary();
            _locationsLoaded = true;
        }
    }

    // see below for other methods
}

ReadWriteLockSlim锁定

public class ReadWriteCache
{
    private static readonly ReaderWriterLockSlim LockSlim =
        new ReaderWriterLockSlim();

    private static bool _locationsLoaded = false;

    private static readonly Dictionary<long, Location> Locations =
        new Dictionary<long, Location>();

    public Location Get(long id)
    {
        EnsureLocationsDictionaryIsPopulated();
        return Locations[id];
    }

    private void EnsureLocationsDictionaryIsPopulated()
    {
        if (Locations.Count > 0 || _locationsLoaded) return;
        LockSlim.EnterWriteLock();
        try
        {
            if (Locations.Count > 0 || _locationsLoaded) return;
            PopulateLocationsDictionary();
            _locationsLoaded = true;
        }
        finally
        {
            LockSlim.ExitWriteLock();
        }
    }

    // see below for other methods
}

两个类都有相同的两种方法:

    private void PopulateLocationsDictionary()
    {
        var items = LoadAllLocationsFromExternalSource();
        if (items == null || items.Count == 0) return;

        for (int i = 0; i < items.Count; i++)
        {
            var location = items[i];
            Locations[location.Id] = location;
        }
    }

    /// <summary>
    /// This method actually calls an external API and takes
    /// at least 5 seconds to run.
    /// </summary>
    /// <returns></returns>
    private List<Location> LoadAllLocationsFromExternalSource()
    {
        return new List<Location>
        {
            new Location
            {Id = 5, Value1 = "one", Value2 = "two", Value3 = "three"},
            new Location
            {Id = 10, Value1 = "I", Value2 = "II", Value3 = "III"},
            new Location
            {Id = 42, Value1 = "un", Value2 = "deux", Value3 = "trois"}
        };
    }

我从这篇文章(When is ReaderWriterLockSlim better than a simple lock?)中看到,当访问模式主要涉及读取时,ReadWriteLockSlim预计会胜过标准锁定。在我的两个场景中仍然如此吗? ReadWriteLockSlimConcurrentDictionary相比如何?我还没有考虑其他因素吗?

1 个答案:

答案 0 :(得分:1)

我建议使用最简单的方法(在这种情况下只是一个ConcurrentDictionary,没有额外的锁)。 ConcurrentDictionary课程的设计完全符合您的要求。

然后我建议通过IDictionary界面将缓存暴露给外界。

如果将来在这个领域出现性能问题(不太可能,瓶颈往往不在您预期的位置),您只需要更改这一段代码,其余的就是申请不会受到影响。

您可能会陷入过早优化的陷阱,这是一个很大的生产力和可维护性杀手。

如果确实想知道哪个更快,请设置测试应用程序,配置文件 2个具有不同负载的情况。对于您的特定情况,您将获得比我们能够在Stack Overflow上提供的更准确的答案!