【问题标题】:Creating thread-safe singleton wrapper for DI container为 DI 容器创建线程安全的单例包装器
【发布时间】:2011-12-24 13:50:51
【问题描述】:

我为我打算在 WPF 应用程序中使用的 Ninject DI 容器创建了一个包装器。我希望它是线程安全的,以防我需要在单独的线程中打开新窗口,但我对使用 volatile 关键字和锁定感到困惑。据我所知,锁定非常容易理解,但我不支持使用 volatile 关键字。从我的谷歌搜索结果中,我了解到 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 或者我可以省略该部分,因为它们在此构造中或多或少是只读的。如果有人能对此有所了解,我将不胜感激。

【问题讨论】:

  • 您可以省略 volatile,原因您已经知道。锁定没问题。另外,当我读到“有人可以撕碎一些光”时,我咯咯地笑着,想着如果只有查克·诺里斯(Chuck Norris)能读到这个:)
  • 好吧,我们都有自己的“特殊时刻”……谢谢您的回答。

标签: c# wpf multithreading .net-4.0 ninject


【解决方案1】:

要实现线程安全的单例模式,基本上有两种选择:

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();

【讨论】:

  • 我从你的回答中不明白我需要同时使用 volatile 关键字和锁定吗?在我的情况下,第二种方法会很好,但我认为在应用程序生命周期中可选地需要昂贵的实例的情况下,这将是开销。感谢您指点我在锁定时仔细检查实例,我忘记了。
  • @Stanque:如果您要使用锁进行延迟初始化,则不需要 volatile,因为锁会引入隐式内存栅栏。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多