【问题标题】:Singleton management implementation and thread safety单例管理实现和线程安全
【发布时间】:2015-04-25 13:49:04
【问题描述】:

我一直在尝试实现单例的方法。我编写了一个小的管理对象,它允许使用更简单、更少代码的方法来编写单例类。

我永远不会在生产系统中使用这样的东西,这有几个原因导致我提出我的问题。

使用下面的代码 - 我假设这个实现会/可能导致线程问题和内存泄漏?我说的对吗?

namespace ConsoleApplication1
{       
    public static class SingletonManager
    {
        private static readonly Dictionary<string, object> Objects;

        static SingletonManager()
        {
            Objects = new Dictionary<string, object>();
        }

        public static T InstanceOf<T>(object[] ctorArgs = null)
            where T : class
        {
            var name = typeof (T).FullName;
            if (Objects.ContainsKey(name))
                return Objects[name] as T;

            var ctor = typeof (T).GetConstructors(
                BindingFlags.Instance |
                BindingFlags.NonPublic)[0];

            var instance = ctor.Invoke(ctorArgs) as T;
            Objects[name] = instance;

            return instance as T;
        }

        public static void DisposeOf<T>()
            where T : Singleton<T>
        {
            Dispose(typeof (T).FullName);
        }

        public static void DisposeOf(Type type)
        {
            Dispose(type.FullName);
        }

        private static void Dispose(string name)
        {
            if (!Objects.ContainsKey(name)) return;

            var obj = Objects[name];
            if (obj is IDisposable)
                ((IDisposable) Objects[name]).Dispose();

            Objects.Remove(name);
        }
    }

    public class Singleton<T>
        where T : class
    {
        private static object ThreadLock = new object();

        public static T Instance(object[] ctorArgs = null)
        {
            lock (ThreadLock)
            {
                return SingletonManager.InstanceOf<T>(ctorArgs);
            }
        }
    }

    public class SomeSingletonClass : Singleton<SomeSingletonClass>
    {
        public int Number;

        private SomeSingletonClass(int i)
        {
            Number = i;
        }
    }

    internal class Program
    {
        private static void Main(string[] args)
        {
            var instance1= SomeSingletonClass.Instance(new object[] {1});

            var instance2 = SomeSingletonClass.Instance(new object[] { 2 });

            //Is false
            var updated = instance1.Number == 2;

            instance2.Number = 99;

            //Is true
            var equals = instance1.Number == instance2.Number;

            //Is true
            var refEquals = ReferenceEquals(instance1, instance2);

            Debugger.Break();
        }    
    }
}

【问题讨论】:

  • 为什么是单身经理?单例应该很少见,并且使用 Lazy&lt;T&gt; 很容易实现。
  • 是的,这是真的。正如我所说,我永远不会在实际系统中使用这样的东西。我今天只是在一个项目中查看一些旧代码,该项目使用了很多单例类,并希望通过简单的实现来玩一玩。
  • 您可能应该改用 CodeReview codereview.stackexchange.com。除非对象实现 IDisposable,否则我会避免命名诸如 Dispose 之类的方法。

标签: c# multithreading memory-leaks singleton


【解决方案1】:
  1. 经典的单例很少是一个好主意。在大多数情况下,您最好只创建一个实例并将其传递给需要它的代码,而不是强制只有一个实例。 IoC 容器将为您完成大部分工作。

  2. 经典单例的实现非常紧凑,无需进一步简化:

    public class MySingleton
    {
        private static Lazy<MySingleton> _instance = new Lazy<MySingleton>(() => new MySingleton());    
        public static MySingleton Instance { get { return _instance.Value; } }
    
        private MySingleton()
        {
    
        }
    }
    

    您最多可以保存其中两行。

  3. 是的,您的代码不是线程安全的。您需要将大部分内容放在 lock 语句中才能解决此问题。

  4. 确定构造函数参数的消费者非常可疑。除非它们都相同,否则您最终会得到不同的实例,具体取决于哪个消费者首先运行。

    这违反了“单一事实来源”原则,是维护和调试的噩梦。

  5. 您的代码依赖于私有反射。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-12-18
    • 1970-01-01
    • 1970-01-01
    • 2018-09-08
    • 2021-05-12
    • 1970-01-01
    • 1970-01-01
    • 2011-01-13
    相关资源
    最近更新 更多