本系列将描述主要的设计模式,使用C#代码进行演示,今天展示的是最简单的单例模式。
单例模式,顾名思义,就是类的实例仅有一个。比如天上的月亮,仅只有1个,不支持使用生成(new)多个,那么我们最简单的写法就是私有化构造函数,然后new一个实例,所有获取实例都通过属性来获取,演示代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CSharpDesignPattern.Singleton
{
/// <summary>
/// 月亮(单例)使用双重锁定
/// </summary>
public class Moon
{
private static Moon _instance=null;
//private static object lockObj = new object();
/// <summary>
/// 私有构造函数,避免调用方初始化
/// </summary>
private Moon()
{
}
/// <summary>
/// 外部访问实例
/// </summary>
public static Moon Instance
{
get
{
if (_instance == null)
{
_instance=new Moon();
}
return _instance;
}
}
}
}
以上代码看上很好,但是在多线程下存在问题,比如第一个线程进入new Moon对象,此时还没有完成,第二个线程已经在获取Moon实例,此时会报未将对象引用设置实例,测试代码如下:
using CSharpDesignPattern.Singleton;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace CSharpDesignPattern.Test.Singleton
{
[TestClass]
public class MoonUnitTest
{
[TestMethod]
public void IsMoonSingleton()
{
var listTask = new List<Task>();
ConcurrentDictionary<int, int> dic = new ConcurrentDictionary<int, int>();
for (int i = 0; i < 100; i++)
{
var task = Task.Factory.StartNew(() =>
{
var instance = Moon.Instance;
dic[instance.GetHashCode()] = i;
});
listTask.Add(task);
}
Task.WaitAll(listTask.ToArray());
Assert.AreEqual(1, dic.Count);
}
}
}
断言失败,我们预期生成一个实例,但是在多线程下,生成了2个实例,一不小心,变成了2个月亮
那么如何确保在多线程下正确呢,这就要使用锁来保护,让new动作变成串行,我们将初始化代码修改如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CSharpDesignPattern.Singleton
{
/// <summary>
/// 月亮(单例)使用双重锁定
/// </summary>
public class Moon
{
private static Moon _instance = null;
private static object lockObj = new object();
/// <summary>
/// 私有构造函数,避免调用方初始化
/// </summary>
private Moon()
{
}
/// <summary>
/// 外部访问实例
/// </summary>
public static Moon Instance
{
get
{
if (_instance == null)
{
lock (lockObj)
{
if (_instance == null)
{
_instance = new Moon();
}
}
}
return _instance;
}
}
}
}
修改后,单元测试顺利通过
当然,我们可以使用更简单的方式,使用static readonly保证类初始化为单例,也是可以顺利通过单元测试的,实例代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CSharpDesignPattern.Singleton
{
/// <summary>
/// 太阳(单例),通过readonly方式构建
/// </summary>
public class Sun
{
private static readonly Sun _instance = new Sun();
/// <summary>
/// 私有构造函数,避免调用方初始化
/// </summary>
private Sun() { }
/// <summary>
/// 外部访问实例
/// </summary>
public static Sun Instance { get { return _instance; } }
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。