潜在的并发问题?

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

private static IDictionary _settingsDictionary = new Dictionary(); public T Settings() where T : ISettings, new() { var key = typeof(T).FullName; if (!_settingsDictionary.ContainsKey(key)) _settingsDictionary[key] = _settingsService.GetSettings(); return (T)_settingsDictionary[key]; } 

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

这在本地测试时工作正常,但我担心它可能会被数百名用户使用。 这使我开始研究ConcurrencyDictionary。 请问你能告诉我是否需要使用它以及如果是这样我会怎么做。

谢谢

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

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

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

您可以使用ConcurrentDictionary.GetOrAdd代替:

 private static ConcurrentDictionary _settingsDictionary = new ConcurrentDictionary(); public T Settings() where T : ISettings, new() { var key = typeof(T).FullName; return _settingsDictionary.GetOrAdd(key, _settingsService.GetSettings()); } 

编辑 :由于您不希望每次都执行_settingsService.GetSettings() ,因此可以选择:

 private static IDictionary _settingsDictionary = new Dictionary(); private static object locker = new object(); public T Settings() where T : ISettings, new() { var key = typeof(T).FullName; lock(locker) { if (!_settingsDictionary.ContainsKey(key)) _settingsDictionary[key] = _settingsService.GetSettings(); return (T)_settingsDictionary[key]; } } 

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

 if (!_settingsDictionary.ContainsKey(key)) 

然后到我们运行时:

 _settingsDictionary[key] = _settingsService.GetSettings(); 

可能有一把钥匙。

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

重要的问题是,“这是一个案例,我们将有很multithreading同时击中字典,或者这是一个罕见但我们需要防范它的情况?”

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

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