1

问题,假设我有线程 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; } } }


    }

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

谢谢,

4

3 回答 3

4

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

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

private static Singleton singletonObject = new Singleton();

很好很简单。

有关在 C# 中实现单例模式(包括Lazy<T>在 .NET 4 中使用)的更多信息,请参阅我关于该主题的文章

于 2012-05-24T19:05:41.890 回答
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 线程 BValue3也设置为 30。

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

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

于 2012-05-24T19:31:40.547 回答
1

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

于 2012-05-24T19:12:04.837 回答