多线程软件设计

时间:2012-05-24 19:02:42

标签: c# multithreading synchronization locking singleton

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


    }

我的问题。而不是具有线程安全属性,是否有更好的方法?

谢谢,

3 个答案:

答案 0 :(得分:4)

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

不要试图以巧妙的方式解决它。只需在静态变量初始值设定项中初始化它:

private static Singleton singletonObject = new Singleton();

美好而简单。

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

答案 1 :(得分: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++,现在值1是11.0 线程A检查值value1是否为10.0 - &gt;返回false! 线程A将Value3设置为30 线程B也将Value3设置为30。

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

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

答案 2 :(得分:1)

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