C# 中使用单例设计模式的惰性加载与 Eager 加载2025年2月5日 | 阅读 8 分钟 C# 中的非延迟或预加载是什么?单例实例通过预加载创建,要么在类需要之前创建,要么在类加载时创建。这保证了实例在请求时即可立即使用,但即使在应用程序的整个生命周期内从未被使用过,它也可能占用资源。 因此,在 C# 中,预加载(也称为非延迟加载)仅仅是在应用程序启动时初始化单例对象,而不是按需创建并将其存储在内存中以备后用。在单例设计模式中使用预加载的优点是:在多线程环境中具有线程安全性,并且对象初始化由公共语言运行时 (CLR) 处理。 这意味着在多线程环境中,我们无需编写任何显式代码来处理线程安全。 注意:根据单例设计模式,一个类保证只有一个实例,并且提供了一个全局访问点。在 C#(或任何其他编程语言)的单例设计模式下,有两种初始化实例的方法:预加载和延迟加载。用于理解单例设计模式中非延迟或预加载的示例让我们通过一个 C# 示例来了解如何使用预加载来实现单例设计模式。将以下代码复制并粘贴到一个名为 Singleton.cs 的类中。在下面的示例中,初始化和准备单实例变量以供将来使用与变量的创建同时完成。这里发生的一切只是对单个实例的预加载。 CLR 将处理多线程环境中的对象初始化和线程安全。通过使用 GetInstance() 方法,我们可以避免编写线程安全、空检查和对象初始化代码,因为 CLR 将负责这些任务。 我们必须在 GetInstance() 方法中返回单例实例。下面的示例代码不言自明,请仔细阅读注释行以获得更好的理解。 接下来,对 Program 类的 Main 方法进行以下修改。这演示了如何使用并行编程。我们使用两个不同的线程访问 Singleton 类的 GetInstance() 方法,这意味着我们在多线程环境中使用 Singleton 实例。请仔细阅读以下示例代码的注释行以获得更好的理解,因为它不言自明。 由于 Count 值为 1,您可以看出只有一个实例被创建。在删除锁变量并进行空检查后,上面的 Singleton 类是线程安全的。当我们在多线程环境中进行预加载时,公共语言运行时 (CLR) 将在内部处理线程安全和变量初始化。 C# 中的延迟加载或推迟加载是什么?顾名思义,延迟加载会等到单例实例真正需要时才创建。如果您希望通过推迟创建实例直到真正需要来节省资源,或者实例初始化成本高昂,这会很有用。 在 C# 中,延迟加载(也称为推迟加载)的想法经常被用来推迟初始化对象,直到它真正需要为止。因此,延迟加载(也称为推迟加载)的主要目标是在需要时才加载对象。 用于理解单例设计模式中延迟或推迟加载的示例在单例设计模式中,延迟加载(通常称为推迟加载)已经介绍过了。在我们之前的文章中,我们介绍了如何使用锁和双重检查锁定机制来实现线程安全的单例设计模式。这两种方法都利用了延迟加载机制,该机制在首次访问对象时创建单例实例。 从那时起,它就使用了已构建的实例。作为开发人员,我们必须编写必要的代码来确保单例实例的线程安全。 从 C# 4.0 开始,我们可以使用 Lazy 泛型类来创建单例实例的延迟加载。让我们继续看看如何使用 C# Lazy 泛型类在单例设计模式中创建延迟加载。 用于理解使用 Lazy 泛型类延迟加载单例实例的示例。.NET Framework 4.0 发布了 Lazy 泛型类,它提供了对按需对象初始化(如果需要按需对象初始化或对象的延迟初始化(例如单例))的内置支持。 要使用 Lazy 泛型类,您必须传入对象的类型(Singleton)。我们必须首先构建实例,如下所示,在 Lazy<T> 构造函数中使用 lambda 表达式。 Lazy<Singleton> SingleInstance = new Lazy<Singleton>(() => new Singleton()); 最重要的是要记住,Lazy 对象默认是线程安全的。当多个线程尝试同时访问同一个 GetInstance 方法时,Lazy 对象会在多线程环境中确保线程安全。因此,为了使用 Lazy 泛型类实现单例实例的延迟加载,请将 Singleton 类修改如下。 在 C# 单例设计模式中何时使用延迟加载与预加载?您的应用程序的限制和需求决定了单例设计模式的预加载与延迟加载选项。在预加载和延迟加载之间进行选择时,请牢记以下几点: 资源使用懒加载 使用延迟加载,仅在需要时才创建单例实例,以节省资源。 这对于初始化成本高昂的对象尤其有用。 预加载 当您拥有充足的资源并希望确保单例实例立即可用时,请使用预加载。成本低廉且轻量级的启动方法可能使预加载更具优势。 初始化成本懒加载 如果单例实例的初始化涉及大量计算或耗时过程,请选择延迟加载。通过推迟这些操作直到需要,延迟加载可以加快应用程序的启动速度。 预加载 如果初始化单例成本低廉且快速,请选择预加载。通过预加载,可以确保在程序启动时实例即可用。 线程安全延迟加载 延迟加载的实现(如 Lazy)本身就是线程安全的。实例只创建一次,并且对其访问是自动同步的。 预加载 如果您在多线程环境中选择此选项,则必须确保实例的初始化是线程安全的。为此,您应该使用锁定机制或其他同步策略。 内存消耗懒加载 延迟加载通过仅在需要时创建单例实例来帮助减少内存使用。在内存优化至关重要的情况下,这可能很有帮助。 预加载 由于实例是提前创建的,预加载会在应用程序启动时使用内存。请验证内存使用情况是否适合您的程序需求。 可预测性懒加载 此技术允许您预测单例实例的创建时间或首次访问时间。系统中的可预测性有助于资源管理。 预加载 在某些实时或关键应用程序中,预加载可能至关重要,因为它确保了实例的即时可用性,从而提供了可预测性。 在 C# 的单例设计模式中决定预加载还是延迟加载时,应考虑您的应用程序需求、初始化成本和线程安全性。两种策略都有其优点和缺点,因此请仔细考虑您的用例以决定哪种最适合。 当然!让我们在 C# 单例模式的上下文中研究预加载与延迟加载,涵盖它们的优点、缺点和推荐做法。 延迟加载优点 资源效率:延迟加载仅在真正需要时才初始化对象,从而节省资源。这对于那些创建成本高昂或资源密集型的对象尤其有用。 提高性能:延迟加载通过推迟初始化直到必要来帮助加快应用程序的启动时间。这对于初始化需要大量处理的对象特别有用。 灵活性:由于延迟加载,可以实现更灵活的初始化选项。例如,您可以异步地或在另一个线程上初始化对象,以进一步最大化效率。 缺点并发问题:延迟加载可能导致并发问题,尤其是在多线程环境中。开发人员必须提供线程安全性,以防止多个线程创建单例的多个实例。 复杂性:由于延迟加载需要锁定或双重检查锁定等同步机制,因此可能会导致代码更复杂。 初始化开销:虽然延迟加载会推迟对象的创建,但首次访问对象时会产生少量开销,因为系统需要检查并可能初始化它。 预加载优点 简洁性:预加载通过消除对同步机制的需求,并在类加载时立即初始化对象,从而简化了代码。 线程安全性:由于实例在类加载时创建,预加载可确保只创建一次实例,因此它默认提供线程安全性。 可预测的性能:预加载提供可预测的性能,因为没有与延迟初始化相关的开销,因为实例在需要时已经初始化。 缺点内存使用增加:对于大型或资源密集型对象,预加载可能会导致更高的内存消耗,因为它即使实例未被使用也会占用内存。 启动速度变慢:如果单例设置过程耗时较长,预加载可能会导致应用程序启动速度变慢。 未使用对象的可能性:如果预先初始化的对象仅在应用程序生命周期内使用,则可能会浪费资源。 最佳实践考虑使用模式:当单例实例可能不会立即使用或根本不使用时,请选择延迟加载。如果应用程序需要单例实例,并且初始化它不会太昂贵,请选择预加载。 确保线程安全性:如果使用延迟加载,请建立正确的同步技术来确保线程安全性。考虑在 .NET 中使用 Lazy 来实现线程安全的延迟初始化。 双重检查锁定结合延迟初始化:如果速度很重要,请对延迟初始化使用双重检查锁定;但是,请确保正确完成以避免竞态条件。 检查资源使用情况:在预加载和延迟加载之间进行选择时,请考虑资源使用情况。虽然预加载会提前使用资源,但延迟加载会节省资源,但可能会导致开销。 结论总之,C# 单例设计模式同时使用了预加载和延迟加载技术,每种技术都有其优点和缺点。预加载与延迟加载之间的选择最终取决于您应用程序的具体需求、资源限制和性能因素。通过了解每种技术的优缺点,您可以有效地应用单例模式来满足您应用程序的需求。 下一个主题C# 中的元数据 |
我们请求您订阅我们的新闻通讯以获取最新更新。