Создание поточно-ориентированной одноэлементной оболочки для контейнера DI - PullRequest
3 голосов
/ 24 декабря 2011

Я создал оболочку для контейнера Ninject DI, которую я намерен использовать в приложении WPF. Я хотел бы, чтобы это было поточно-ориентированным на случай, если мне понадобится открывать новые окна в отдельных потоках, но я запутался в использовании ключевого слова volatile и блокировки. Насколько я знаю, блокировка довольно проста для понимания, но я не склонен использовать ключевое слово volatile . Исходя из результатов поиска в Google, я пришел к выводу, что ключевое слово volatile обеспечивает безопасный доступ для чтения для указанного экземпляра в многопоточной среде, но не обеспечивает никакой безопасности при внесении изменений в пространство памяти, занимаемое указанным экземпляром. Я объединил свое решение с некоторыми примерами потоковых одноэлементных шаблонов и придумал эту оболочку, которая послужила бы мне локатором службы:

public class NinjectResolver
{
    private static object syncRoot = new object();
    private static volatile NinjectResolver instance = null;
    private static volatile IKernel kernel = null;

    public static NinjectResolver GetInstance()
    {
        lock (syncRoot)
        {
            if (instance == null)
                instance = new NinjectResolver();
        }
        return instance;
    }

    static NinjectResolver()
    {
        lock (syncRoot)
        {
            if (kernel == null)
                kernel = new StandardKernel();
        }
    }

    public void AddBindings(Dictionary<Type, Type> bindings)
    {
        lock (syncRoot)
        {
            foreach (var binding in bindings)
            {
                Type IType = binding.Key;
                Type ImplementationType = binding.Value;
                kernel.Bind(IType).To(ImplementationType);
            }
        }
    }

    private NinjectResolver()
    {
    }

    /// <summary>
    /// Resolves All dependencies for requested instance and returns that instance
    /// </summary>
    /// <typeparam name="T">Requested Implementation type</typeparam>
    /// <returns>Instance of Implementation type</returns>
    public T Resolve<T>()
    {
        return kernel.TryGet<T>();
    }

    /// <summary>
    /// Resolves property injection dependencies in already instantiated Implementation types
    /// </summary>
    /// <param name="obj">Specified instance of implementation type</param>
    public void Inject(object obj)
    {
        kernel.Inject(obj);
    }
}

У меня такой вопрос: нужно ли использовать блокировку в указанных местах, поскольку инициализация будет происходить внутри App.xaml.cs (первый вызов GetInstance () ) и сделать эти статические поля должны быть объявлены как volatile , или я могу опустить эту часть, так как они более или менее доступны только для чтения в этой конструкции. Буду признателен, если кто-нибудь сможет пролить свет на это.

1 Ответ

3 голосов
/ 24 декабря 2011

Чтобы реализовать потокобезопасный одноэлементный шаблон, у вас есть два основных варианта:

1. Двойная проверка блокировки

public static NinjectResolver GetInstance()
{   
    if(instance == null)
    {
        lock (syncRoot)
        {
            if (instance == null)
                instance = new NinjectResolver();
        }
    }
    return instance;
}

2. Инициализация экземпляра при объявлении

private static volatile NinjectResolver instance = new NinjectResolver();

public static NinjectResolver GetInstance()
{
    return instance;
}

Также вы можете оставить код внутри статического блока и просто использовать:

private static volatile IKernel kernel = new StandardKernel();
...