在C#编程中,单例模式(Singleton Pattern)是一种极其实用且常见的设计模式。它确保一个类只有一个实例,并提供一个全局访问点。本文将深入剖析单例模式的原理、实现方式和实际应用场景,帮助你全面掌握这一重要的设计模式。
单例模式是一种创建型设计模式,它确保一个类仅有一个实例,并提供一个全局访问点。简单来说,如果你需要一个类在整个应用程序中只存在一个对象(比如日志管理器、配置处理器或共享资源),单例模式正是你所需要的。
在以下情况下,单例模式是理想的选择:
C#namespace AppSingleton
{
public class Singleton
{
private static Singleton instance = null;
private Singleton()
{
Console.WriteLine("单例实例已创建");
}
public static Singleton GetInstance()
{
if (instance == null)
{
instance = new Singleton();
}
return instance;
}
public void SomeBusinessLogic()
{
Console.WriteLine("执行业务逻辑");
}
}
internal class Program
{
static void Main(string[] args)
{
Singleton s1 = Singleton.GetInstance();
Singleton s2 = Singleton.GetInstance();
// 验证两个引用指向同一对象
Console.WriteLine("s1和s2是否是同一实例: " + (s1 == s2));
s1.SomeBusinessLogic();
Console.ReadKey();
}
}
}
上面的基本实现在多线程环境中可能会出问题。如果两个线程同时检查instance == null
并尝试创建实例,可能会导致创建多个实例。
C#namespace AppSingleton
{
public class ThreadSafeSingleton
{
private static ThreadSafeSingleton instance = null;
private static readonly object lockObj = new object();
private ThreadSafeSingleton()
{
Console.WriteLine("线程安全的单例实例已创建");
}
public static ThreadSafeSingleton GetInstance()
{
lock (lockObj) // 确保线程安全
{
if (instance == null)
{
instance = new ThreadSafeSingleton();
}
}
return instance;
}
}
internal class Program
{
static void Main(string[] args)
{
ThreadSafeSingleton s1 = ThreadSafeSingleton.GetInstance();
ThreadSafeSingleton s2 = ThreadSafeSingleton.GetInstance();
// 验证两个引用指向同一对象
Console.WriteLine("s1和s2是否是同一实例: " + (s1 == s2));
Console.ReadKey();
}
}
}
C#using System.Collections.Concurrent;
namespace AppSingleton
{
public sealed class DoubleCheckSingleton
{
private static DoubleCheckSingleton instance = null;
private static readonly object lockObj = new object();
private DoubleCheckSingleton() { }
public static DoubleCheckSingleton GetInstance()
{
// 第一次检查
if (instance == null)
{
// 加锁
lock (lockObj)
{
// 第二次检查
if (instance == null)
{
instance = new DoubleCheckSingleton();
}
}
}
return instance;
}
}
internal class Program
{
static void Main(string[] args)
{
const int threadCount = 5;
var instances = new ConcurrentBag<DoubleCheckSingleton>();
var tasks = new List<Task>();
// 创建多个任务同时获取单例
for (int i = 0; i < threadCount; i++)
{
int taskId = i;
tasks.Add(Task.Run(() =>
{
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] 任务{taskId}开始 - 线程ID: {Thread.CurrentThread.ManagedThreadId}");
// 添加随机延迟,增加并发冲突的可能性
Thread.Sleep(new Random().Next(10, 50));
var instance = DoubleCheckSingleton.GetInstance();
instances.Add(instance);
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] 任务{taskId}完成,获得实例哈希码: {instance.GetHashCode()} - 线程ID: {Thread.CurrentThread.ManagedThreadId}");
}));
}
// 等待所有任务完成
Task.WaitAll(tasks.ToArray());
// 验证所有实例是否相同
var instanceArray = instances.ToArray();
Console.WriteLine($"\n并发测试结果:");
Console.WriteLine($"总共获得 {instanceArray.Length} 个实例");
}
}
}
C#提供了Lazy<T>
类,可以轻松实现线程安全的延迟初始化:
C#using System.Collections.Concurrent;
namespace AppSingleton
{
public class LazySingleton
{
private static readonly Lazy<LazySingleton> instance =
new Lazy<LazySingleton>(() => new LazySingleton());
private LazySingleton()
{
Console.WriteLine("Lazy单例实例已创建");
}
public static LazySingleton Instance => instance.Value;
public void DoWork()
{
Console.WriteLine("Lazy单例执行工作...");
}
}
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("程序开始,单例尚未创建");
// 首次访问时创建实例
LazySingleton singleton = LazySingleton.Instance;
singleton.DoWork();
// 再次访问不会创建新实例
LazySingleton anotherReference = LazySingleton.Instance;
Console.WriteLine("是否是同一实例: " + (singleton == anotherReference));
}
}
}
方式 | 描述 | 适用场景 |
---|---|---|
饿汉式 | 实例在应用程序启动时创建 | 性能关键场景,初始化开销较小 |
懒汉式 | 实例在首次需要时创建 | 节省内存和启动时间,初始化开销大 |
C#using System.Collections.Concurrent;
namespace AppSingleton
{
public sealed class EagerSingleton
{
private static readonly EagerSingleton instance = new EagerSingleton();
private EagerSingleton()
{
Console.WriteLine("饿汉式单例在程序启动时已创建");
}
// 公共属性
public static EagerSingleton Instance
{
get { return instance; }
}
}
internal class Program
{
static void Main(string[] args)
{
var s1 = EagerSingleton.Instance;
var s2 = EagerSingleton.Instance;
var s3 = EagerSingleton.Instance;
Console.WriteLine($"s1 哈希码: {s1.GetHashCode()}");
Console.WriteLine($"s2 哈希码: {s2.GetHashCode()}");
Console.WriteLine($"s3 哈希码: {s3.GetHashCode()}");
Console.WriteLine($"s1 == s2: {s1 == s2}");
Console.WriteLine($"s1 == s3: {s1 == s3}");
Console.WriteLine($"ReferenceEquals(s1, s2): {ReferenceEquals(s1, s2)}");
Console.ReadKey();
}
}
}
使用单例模式时应该注意以下几点:
单例模式是C#中一种强大且常用的设计模式,它确保类只有一个实例并提供全局访问点。使用单例模式时要注意以下几点:
Lazy<T>
实现延迟加载,提高性能单例模式在日志记录、配置管理、数据库连接池和缓存系统等场景中特别有用。通过正确实现和应用单例模式,你可以有效控制资源使用,优化应用性能,并简化全局状态管理。
希望本文对你理解和应用C#单例模式有所帮助!如果你有任何问题或建议,欢迎在评论区留言讨论。
关键词: C#单例模式, 设计模式, 线程安全单例, Lazy初始化, 饿汉式, 懒汉式, 日志记录器, 配置管理, 数据库连接, 缓存机制, C#编程技巧
本文作者:技术老小子
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!