.Net 字典在极少数情况下不会在插入后直接获取值
.Net dictionary doesn't fetch value directly after inserting on rare occasions
我编写了以下代码片段:
public interface IModelToViewModelServiceBase<in TDomain, out TViewModel>
where TDomain : class, IDataModel
where TViewModel : class, IDataModelViewModel
{
TViewModel GetViewModel(TDomain model);
}
public abstract class ModelToViewModelServiceBase<TDomain, TViewModel> : IModelToViewModelServiceBase<TDomain, TViewModel>
where TDomain : class, IDataModel
where TViewModel : class, IDataModelViewModel
{
private readonly IDictionary<TDomain, TViewModel> _modelToViewModel
= new Dictionary<TDomain, TViewModel>();
protected abstract TViewModel Create(TDomain model);
public TViewModel GetViewModel(TDomain model)
{
if (model == null) return null;
if (!_modelToViewModel.ContainsKey(model))
_modelToViewModel[model] = Create(model);
return _modelToViewModel[model];
}
}
此 class 的目的与问题无关。在极少数情况下,我会在 return 行上收到 KeyNotFound。但是,根据我的理解,前面的 if 子句应该可以防止这种情况发生。没有键可能为 null,并且在之前的指令中添加了检索到的值(如果之前不存在)。
我在这里错过了什么?
我现在开发了一个解决方法:
public interface IModelToViewModelServiceBase<in TDomain, out TViewModel>
where TDomain : class, IDataModel
where TViewModel : class, IDataModelViewModel
{
TViewModel GetViewModel(TDomain model);
}
public abstract class ModelToViewModelServiceBase<TDomain, TViewModel> : IModelToViewModelServiceBase<TDomain, TViewModel>
where TDomain : class, IDataModel
where TViewModel : class, IDataModelViewModel
{
private readonly IDictionary<TDomain, TViewModel> _modelToViewModel
= new Dictionary<TDomain, TViewModel>();
protected abstract TViewModel Create(TDomain model);
public TViewModel GetViewModel(TDomain model)
{
if (model == null) return null;
TViewModel viewModel = null;
if (!_modelToViewModel.ContainsKey(model))
{
viewModel = Create(model);
_modelToViewModel[model] = viewModel;
}
else
viewModel = _modelToViewModel[model];
return viewModel;
}
}
这似乎有效。但是,这种解决方法应该不是必需的。可能这个解决方法更好,因为现在执行的字典访问减少了一次。
尽管如此,以前的版本应该一直有效。
回答后更新:
@evk 和@mjwills 都是对的。我不认为我的代码对于并发使用是不安全的,并且多个线程正在访问它。因此,根据@mjwills 的建议,代码如下所示:
public interface IModelToViewModelServiceBase<in TDomain, out TViewModel>
where TDomain : class, IDataModel
where TViewModel : class, IDataModelViewModel
{
TViewModel GetViewModel(TDomain model);
}
public abstract class ModelToViewModelServiceBase<TDomain, TViewModel> : IModelToViewModelServiceBase<TDomain, TViewModel>
where TDomain : class, IDataModel
where TViewModel : class, IDataModelViewModel
{
private readonly ConcurrentDictionary<TDomain, TViewModel> _modelToViewModel
= new ConcurrentDictionary<TDomain, TViewModel>();
protected abstract TViewModel Create(TDomain model);
public TViewModel GetViewModel(TDomain model)
{
if (model == null) return null;
return _modelToViewModel.GetOrAdd(model, Create); ;
}
}
您提到您的代码可以从多个线程访问,但您的代码不是线程安全的。从多个线程写入和读取常规 Dictionary
是不安全的(如果你只读而从不写 - 那么没关系)。您可能会认为,如果您从不从字典中删除项目,那么 KeyNotFoundException
可能不会在您的情况下抛出,但事实并非如此。当您以未设计的方式使用结构时 - 任何事情 都可能发生。例如考虑这个代码:
class Program
{
public static void Main(string[] args)
{
var service = new ModelToViewModelServiceBase();
new Thread(() => AddServices(service)).Start();
new Thread(() => AddServices(service)).Start();
new Thread(() => AddServices(service)).Start();
new Thread(() => AddServices(service)).Start();
Console.ReadKey();
}
private static void AddServices(ModelToViewModelServiceBase services) {
for (int i = 0; i < 100000; i++) {
services.GetViewModel(i);
}
}
}
public class ModelToViewModelServiceBase {
private readonly IDictionary<int, string> _modelToViewModel
= new Dictionary<int, string>();
protected string Create(int model) {
return model.ToString();
}
public string GetViewModel(int model) {
if (!_modelToViewModel.ContainsKey(model))
_modelToViewModel[model] = Create(model);
return _modelToViewModel[model];
}
}
当你 运行 它时 - 你几乎总是会得到 KeyNotFoundException
,而你永远不会从字典中删除项目。那是因为 Dictionary
内部是如何实现的,我认为确切的细节与这个问题无关。
长话短说 - 不要在没有适当同步的情况下使用来自多线程的非线程安全结构(除非所有线程只读而从不写),即使您可能觉得它会起作用很好。
我编写了以下代码片段:
public interface IModelToViewModelServiceBase<in TDomain, out TViewModel>
where TDomain : class, IDataModel
where TViewModel : class, IDataModelViewModel
{
TViewModel GetViewModel(TDomain model);
}
public abstract class ModelToViewModelServiceBase<TDomain, TViewModel> : IModelToViewModelServiceBase<TDomain, TViewModel>
where TDomain : class, IDataModel
where TViewModel : class, IDataModelViewModel
{
private readonly IDictionary<TDomain, TViewModel> _modelToViewModel
= new Dictionary<TDomain, TViewModel>();
protected abstract TViewModel Create(TDomain model);
public TViewModel GetViewModel(TDomain model)
{
if (model == null) return null;
if (!_modelToViewModel.ContainsKey(model))
_modelToViewModel[model] = Create(model);
return _modelToViewModel[model];
}
}
此 class 的目的与问题无关。在极少数情况下,我会在 return 行上收到 KeyNotFound。但是,根据我的理解,前面的 if 子句应该可以防止这种情况发生。没有键可能为 null,并且在之前的指令中添加了检索到的值(如果之前不存在)。
我在这里错过了什么?
我现在开发了一个解决方法:
public interface IModelToViewModelServiceBase<in TDomain, out TViewModel>
where TDomain : class, IDataModel
where TViewModel : class, IDataModelViewModel
{
TViewModel GetViewModel(TDomain model);
}
public abstract class ModelToViewModelServiceBase<TDomain, TViewModel> : IModelToViewModelServiceBase<TDomain, TViewModel>
where TDomain : class, IDataModel
where TViewModel : class, IDataModelViewModel
{
private readonly IDictionary<TDomain, TViewModel> _modelToViewModel
= new Dictionary<TDomain, TViewModel>();
protected abstract TViewModel Create(TDomain model);
public TViewModel GetViewModel(TDomain model)
{
if (model == null) return null;
TViewModel viewModel = null;
if (!_modelToViewModel.ContainsKey(model))
{
viewModel = Create(model);
_modelToViewModel[model] = viewModel;
}
else
viewModel = _modelToViewModel[model];
return viewModel;
}
}
这似乎有效。但是,这种解决方法应该不是必需的。可能这个解决方法更好,因为现在执行的字典访问减少了一次。 尽管如此,以前的版本应该一直有效。
回答后更新:
@evk 和@mjwills 都是对的。我不认为我的代码对于并发使用是不安全的,并且多个线程正在访问它。因此,根据@mjwills 的建议,代码如下所示:
public interface IModelToViewModelServiceBase<in TDomain, out TViewModel>
where TDomain : class, IDataModel
where TViewModel : class, IDataModelViewModel
{
TViewModel GetViewModel(TDomain model);
}
public abstract class ModelToViewModelServiceBase<TDomain, TViewModel> : IModelToViewModelServiceBase<TDomain, TViewModel>
where TDomain : class, IDataModel
where TViewModel : class, IDataModelViewModel
{
private readonly ConcurrentDictionary<TDomain, TViewModel> _modelToViewModel
= new ConcurrentDictionary<TDomain, TViewModel>();
protected abstract TViewModel Create(TDomain model);
public TViewModel GetViewModel(TDomain model)
{
if (model == null) return null;
return _modelToViewModel.GetOrAdd(model, Create); ;
}
}
您提到您的代码可以从多个线程访问,但您的代码不是线程安全的。从多个线程写入和读取常规 Dictionary
是不安全的(如果你只读而从不写 - 那么没关系)。您可能会认为,如果您从不从字典中删除项目,那么 KeyNotFoundException
可能不会在您的情况下抛出,但事实并非如此。当您以未设计的方式使用结构时 - 任何事情 都可能发生。例如考虑这个代码:
class Program
{
public static void Main(string[] args)
{
var service = new ModelToViewModelServiceBase();
new Thread(() => AddServices(service)).Start();
new Thread(() => AddServices(service)).Start();
new Thread(() => AddServices(service)).Start();
new Thread(() => AddServices(service)).Start();
Console.ReadKey();
}
private static void AddServices(ModelToViewModelServiceBase services) {
for (int i = 0; i < 100000; i++) {
services.GetViewModel(i);
}
}
}
public class ModelToViewModelServiceBase {
private readonly IDictionary<int, string> _modelToViewModel
= new Dictionary<int, string>();
protected string Create(int model) {
return model.ToString();
}
public string GetViewModel(int model) {
if (!_modelToViewModel.ContainsKey(model))
_modelToViewModel[model] = Create(model);
return _modelToViewModel[model];
}
}
当你 运行 它时 - 你几乎总是会得到 KeyNotFoundException
,而你永远不会从字典中删除项目。那是因为 Dictionary
内部是如何实现的,我认为确切的细节与这个问题无关。
长话短说 - 不要在没有适当同步的情况下使用来自多线程的非线程安全结构(除非所有线程只读而从不写),即使您可能觉得它会起作用很好。