2

我一直在构建 ASP.NET MVC 应用程序,当我启动它时我担心潜在的多线程问题。一个特别关注的是以下代码:

private static IDictionary<string, ISettings> _settingsDictionary = new Dictionary<string, ISettings>();

public T Settings<T>() where T : ISettings, new() {
    var key = typeof(T).FullName;

    if (!_settingsDictionary.ContainsKey(key))
        _settingsDictionary[key] = _settingsService.GetSettings<T>();

    return (T)_settingsDictionary[key];
}

注意字典被定义为静态的。这允许我缓存字典,以便它为应用程序长度的每个请求返回相同的实例。

这在本地测试时效果很好,但我担心数百名用户使用它时可能会受到影响。这导致我调查 ConcurrencyDictionary。请您告诉我是否需要使用它以及如果是这种情况我将如何使用它。

谢谢

4

2 回答 2

6

是的,这里存在潜在的数据竞争:

if (!_settingsDictionary.ContainsKey(key))
    _settingsDictionary[key] = _settingsService.GetSettings<T>();

这可能导致两个线程添加相同的键,因为它们可以在任何时候被中断。

您可以改用ConcurrentDictionary.GetOrAdd

private static ConcurrentDictionary<string, ISettings> _settingsDictionary = new ConcurrentDictionary<string, ISettings>();

public T Settings<T>() where T : ISettings, new() {
    var key = typeof(T).FullName;

    return _settingsDictionary.GetOrAdd(key, _settingsService.GetSettings<T>());
}

编辑:由于您不想_settingsService.GetSettings<T>()每次都被执行,因此可以选择:

private static IDictionary<string, ISettings> _settingsDictionary = new Dictionary<string, ISettings>();
private static object locker = new object();

public T Settings<T>() where T : ISettings, new() {
    var key = typeof(T).FullName;
    lock(locker) 
    {
        if (!_settingsDictionary.ContainsKey(key))
            _settingsDictionary[key] = _settingsService.GetSettings<T>();

        return (T)_settingsDictionary[key];
    }
}
于 2012-08-29T10:50:29.753 回答
2

是的,有一场比赛,因为如果在以下位置找不到钥匙:

if (!_settingsDictionary.ContainsKey(key))

然后在我们运行时:

_settingsDictionary[key] = _settingsService.GetSettings<T>();

可能有一把钥匙。

它甚至比只是不必要地更换更糟糕。如果线程 1 添加键需要调整大小,那么当线程 2 添加并且它被认为需要调整大小时,它可能是部分完成,并且几乎所有的赌注都没有进一步使用该字典。

重要的问题是,“这是我们将有大量线程同时访问字典的情况,还是这种情况很少见但我们需要防范它?”

在第一种情况下,使用 ConcurrentDictionary。在第二种情况下,只需为您当前的代码添加一个锁。ConcurrentDictionary 在并发方面提供了更好的性能(正如人们从名称中所期望的那样),但是当通常只有一个线程实际命中它时,围绕普通字典的锁会更好,但偶尔的并发调用是可能的。

替代两者,如果可能的设置数量很少,只需在开始时加载批次。如果没有更多的写作,字典对多个阅读者来说是安全的,并且零锁定是最快的。

于 2012-08-29T11:15:54.323 回答