【问题标题】:Multi-threaded software design多线程软件设计
【发布时间】:2012-05-24 19:02:42
【问题描述】:

问题,假设我有线程 A 和线程 B,它们都需要访问单例对象及其属性。

目前单例如下所示。

public class Singleton{

        #region fields
        private static Singleton singletonObject;
        private double value1= 0;
        private double value2= 0;
        private double value3= 0;
        private double value4= 0;
        private object locker = null;
        #endregion

        // private constructor. This will avoid creating object using new keyword
        private Singleton() {
            locker = new object();
        }

        // public method which will be called
        public void GetName() {
            Console.WriteLine("singleton Object");
        }
        public static Singleton Instance() {
            // this object will be used with lock, so that it will be always one thread which will be executing the code
            object instanceLocker = new object();
            // put a lock on myObject. We won't be able to use singleTonObject becuase it will be null. lock is to make the object thread safe.
            // lock can't be worked with null objects.
            lock (instanceLocker) {
                // check whether the instance was there. If it's not there, then create an instance.
                if (singletonObject == null) {
                    singletonObject = new Singleton();

                }
            }
            return singletonObject;
        }

        public double Value1 { get { lock (locker) { return value1; } } set { lock (locker) { value1= value; } } }
        public double Value2 { get { lock (locker) { return value2; } } set { lock (locker) { value2= value; } } }
        public double Value3 { get { lock (locker) { return value3; } } set { lock (locker) { value3= value; } } }
        public double Value4 { get { lock (locker) { return value4; } } set { lock (locker) { value4= value; } } }


    }

我的问题。除了拥有线程安全的属性,还有更好的方法吗?

谢谢,

【问题讨论】:

    标签: c# multithreading synchronization locking singleton


    【解决方案1】:

    目前您的代码已完全损坏。您正在创建一个新对象以在每次调用期间锁定。没有其他线程会知道它,所以它完全没有意义。

    不要费心尝试以聪明的方式修复它。只需在静态变量初始化器中初始化它:

    private static Singleton singletonObject = new Singleton();
    

    漂亮又简单。

    有关在 C# 中实现单例模式的更多信息(包括在 .NET 4 中使用Lazy<T>),请参阅my article on the topic

    【讨论】:

    • +1 我刚刚得出了同样的结论。哦,还有 DoubleDunk,真的没有比这更好的通用解决方案了。对于您的具体情况,可能会有更好的方法,但是我们需要更多地了解您使用这个类的目的。
    • 感谢您的回答。那么,基本上是在静态单例初始化之后而不是在构造函数中初始化locker对象?
    • 乔恩,非常感谢您的回答。我在您的文章中实施了第二个解决方案。但是,我喜欢第 4 种解决方案。我非常感谢你,我会为你的文章添加书签。再次感谢。
    • @DoubleDunk:你真的需要真的懒惰地初始化吗?第四种方法是我通常使用的方法。至于locker - 破碎是由于instanceLocker,而不是locker
    • 是的,我知道。我摆脱了 instanceLocker 并坚持使用储物柜。我喜欢第 4 种方法并将实施它。再次感谢。
    【解决方案2】:

    除了您为每次调用创建一个新对象来锁定之外,还有另一个基本问题:即使您确实拥有相同的对象,您仍然没有真正保护任何东西。

    沿线某处将Value1 初始化为 9:

    Singleton.Instance().Value1 = 9;
    

    现在假设您有两个线程执行此代码:

    public void Foo()
    {
        Singleton.Instance().Value1++;
    
        if(Singleton.Instance().Value1==10.0)
        {
             Singleton.Instance().Value2 = 20.0;
        }
        else
        {
             Singleton.Instance().Value3 = 30.0;
        }
    }
    

    线程 A 调用 Value1++ 并将 value1 递增到 10.0 线程 B 调用 Value1++ 现在 value1 是 11.0 线程 A 检查 value1 的值是否为 10.0 -> 返回 false! 线程 A 将 Value3 设置为 30 线程 B 也将 Value3 设置为 30。

    这只是一个非常简单的示例,其中锁定属性不会保护您,因为外部代码无法保证读取或写入内容的顺序。线程 A 和线程 B 的执行顺序可能有许多其他顺序,这将导致完全不同的结果。

    这种行为可能没问题,因为您可以让Singleton 类的用户负责确保在您的类之外进行正确操作,但这通常是您应该注意的。简单地锁定属性并不能消除读/写争用。

    【讨论】:

    • 非常好的一点,在我的开发过程中我会留意的。非常感谢。
    【解决方案3】:

    您使用的是 .NET 4.0 吗?您可以将 ConCurrent 集合用于线程安全活动,而不是锁定。

    【讨论】:

    • 我正在使用 3.5,但没有任何东西阻止我使用 4.0。谢谢你的意见,我会调查的。
    • 我只是在这里没有看到 singletoncollection 之间的链接。
    • @BrianGideon Singleton 有 4 个双打。与其单独存储它们,不如使用一个并发集合,其中仅包含 4 个(双)值。
    • @Servy:是的……确实。我想我没有考虑到这一点。不过,这似乎仍然有些牵强。
    猜你喜欢
    • 2015-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多