C# 中的线程安全单例设计模式(附示例)

2025年2月5日 | 阅读 4 分钟

C# 单例设计模式中的懒初始化是什么?

这意味着单例实例仅在我们调用单例的 GetInstance 方法后才会被创建。在单例设计模式中,这种创建单例实例的延迟称为懒初始化。

在单线程环境中,GetInstance 方法会在我们需要时调用单例类的懒初始化或按需对象构造。然而,它在多线程环境中将无法按预期工作。当多个线程同时并行使用 GetInstance 方法时——正如我们刚才讨论过的——懒初始化可能导致在多线程环境中创建多个单例类的实例。

如何在 C# 中实现线程安全的单例设计模式?

  • 使用锁实现线程安全的单例。双重检查锁定用于实现线程安全的单例设计模式。
  • 使用急切加载实现线程安全的单例设计模式。懒加载是使用 Lazy 泛型类在单例设计模式中实现的。
  • 我们将评估之前描述的每种 C# 线程安全单例设计模式的实现方法。

使用锁在 C# 中实现线程安全的单例设计模式。

让我们来谈谈如何在 C# 中使用锁来构建线程安全的单例设计模式。请记住,在多线程环境中,锁是管理单例实例的理想方式。我们可以通过使用锁来同步方法,以限制一次只有一个线程可以访问它。

请对单例类进行以下修改,使其更具意义。下面的代码使用 lock 对象来锁定共享资源并验证实例是否已被创建。如果实例已经创建,我们返回实例;否则,我们创建实例然后返回它。

使用双重检查锁定机制在 C# 中实现线程安全的单例设计模式。

首先,我们将验证实例是否已在用于在 C# 中实现线程安全单例设计模式的双重检查锁定机制中创建。如果尚未创建,我们将仅使用锁来同步方法。为了更好地理解,请对单例类进行以下修改。正如你所见,我们通过运行两次来验证实例是否为 null。

如果实例不为 null,我们将返回单例实例。如果实例为 null,我们将使用锁来确保只有一个线程可以进入关键部分。为了确保只创建一个单例类的实例,我们在锁块内验证 null 条件。请仔细阅读以下示例代码的注释行以获得更好的理解,因为它是不言自明的。

使用 Lazy 实现的线程安全单例

在 C# 中,有几种方法可以实现线程安全的单例。由于其天生的线程安全性,一种流行且推荐的懒初始化方法是使用 Lazy 类型。

何时在 C# 中使用线程安全的单例设计模式?

当您需要确保一个类在程序的整个生命周期中只有一个实例,并且您预计多个线程将同时访问该数据时,请记住在 C# 中使用线程安全的单例布局模式。在线程安全单例设计至关重要的场景中:

  • 共享资源管理: 您程序的多个组件需要共享一个资源,例如数据库连接池、配置管理器或日志服务。
  • 并发控制: 当多个线程可能同时尝试访问单例实例时,您必须确保其初始化是安全的且是原子的。
  • 资源效率: 您希望通过仅在首次访问单例实例时创建它来优化资源利用率,而不是在应用程序启动时创建它。
  • 懒初始化: 通过等到真正需要时才创建单例实例,可以提高效率和资源利用率。

以下特定情况需要线程安全单例模式的实用性:

  • 管理数据库池: 数据库连接池是管理一组希望在多个软件组件之间共享的数据库连接的过程。
  • 日志记录: 确保即使在多个线程同时记录时,日志消息也能一致地发布到单个日志文件或位置。
  • 配置管理: 应用程序配置设置的集中存储和检索。
  • 缓存: 部署缓存管理器以在内存中存储频繁使用的信息。
  • 资源管理: 资源管理涉及限制对有限资源的访问,例如网络连接或硬件组件。

结论

如果您希望确保一个类在多个线程中只有一个实例,则必须在 C# 中实现线程安全的单例设计模式。

通过使用双重检查锁定技术,我们可以安全、高效地实例化单例实例,并最大限度地减少同步开销。

请记住,虽然单例设计提供了一种获取类单个实例的可行方法,但它应该只在很少使用。过度使用单例会导致代码耦合过紧,难以测试和更新。

在部署单例之前,请务必确保它确实是您用例所必需的。