潜在的并发问题?

时间:2012-08-29 10:48:15

标签: c# asp.net-mvc multithreading concurrency

我一直在构建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。如果是这样的话,请你告诉我是否需要使用它以及我将如何这样做。

由于

2 个答案:

答案 0 :(得分: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];
    }
}

答案 1 :(得分:2)

是的,有一场比赛,因为如果没有找到钥匙:

if (!_settingsDictionary.ContainsKey(key))

然后到我们跑的时候:

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

可能有一把钥匙。

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

重要的问题是,“这是一个案例,我们会有很多线程同时击中字典,或者这种情况很少见,但我们需要防范它吗?”

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

两者的替代方案,如果可能的设置数量很少,只需在开头加载该批次。如果没有更多的写作,字典对于多个读者是安全的,并且在零锁定的情况下,它是最快的。