.Net字典在极少数情况下插入后不会直接获取值

问题描述 投票:1回答:1

我写了以下代码片段:

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];
    }
}

这个类的目的与问题无关。在极少数情况下,我会在返回线上获得KeyNotFound。但是,根据我的理解,前面的if-clause应该可以防止这种情况发生。没有键可能为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和@mwwills都是对的。我不认为我的代码对于并发使用是不安全的,并且多个线程正在访问它。因此,在@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); ;
    }
}
c# .net dictionary keynotfoundexception
1个回答
3
投票

您提到可以从多个线程访问您的代码,但您的代码不是线程安全的。从多个线程写入和读取常规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在内部实施,我认为确切的细节与这个问题无关。

简而言之 - 只是不使用来自多个线程的非线程安全结构(除非所有线程只读取并且永远不会写入)而没有正确的同步,即使它可能会让您觉得它可以正常工作。

© www.soinside.com 2019 - 2024. All rights reserved.