在C#开发中,Lazy<T> 是一个非常实用的泛型类,用于实现延迟初始化(Lazy Initialization)。它允许我们在第一次真正需要某个对象时才创建它,从而提升程序性能、节省内存资源。然而,在多线程环境下,如何确保 Lazy<T> 的线程安全性就变得尤为重要。

简单来说,Lazy<T> 封装了一个值,该值在首次访问其 Value 属性时才会被创建。这种“按需创建”的机制非常适合用于初始化成本较高的对象。
var lazyValue = new Lazy<string>(() => { Console.WriteLine("正在初始化..."); return "Hello, Lazy!";});Console.WriteLine(lazyValue.IsValueCreated); // FalseConsole.WriteLine(lazyValue.Value); // 输出:正在初始化... \n Hello, Lazy!Console.WriteLine(lazyValue.IsValueCreated); // True在多线程环境中,多个线程可能同时尝试访问 Lazy<T>.Value。为了避免竞态条件(Race Condition),C# 提供了三种线程安全模式,通过 LazyThreadSafetyMode 枚举来控制:
适用于确定只在单线程中使用的场景。
var lazy = new Lazy<ExpensiveObject>( () => new ExpensiveObject(), LazyThreadSafetyMode.None);允许多个线程并发初始化,但最终所有线程看到的是同一个实例(第一个完成初始化的)。
var lazy = new Lazy<ExpensiveObject>( () => new ExpensiveObject(), LazyThreadSafetyMode.PublicationOnly);注意:此模式下可能会创建多个 ExpensiveObject 实例,但只有第一个会被保留,其余会被垃圾回收。
这是 Lazy<T> 的默认行为。它使用内部锁确保初始化代码只执行一次,是大多数多线程场景的首选。
// 默认就是 ExecutionAndPublicationvar lazy = new Lazy<ExpensiveObject>(() => new ExpensiveObject());// 显式指定var lazy2 = new Lazy<ExpensiveObject>( () => new ExpensiveObject(), LazyThreadSafetyMode.ExecutionAndPublication);选择哪种模式取决于你的应用场景:
下面是一个模拟多线程访问 Lazy<T> 的例子:
using System;using System.Threading;using System.Threading.Tasks;class Program{ static void Main() { var lazy = new Lazy<string>(() => { Thread.Sleep(1000); // 模拟耗时操作 return $"初始化于线程 {Thread.CurrentThread.ManagedThreadId}"; }); // 启动5个任务并发访问 var tasks = new Task[5]; for (int i = 0; i < 5; i++) { tasks[i] = Task.Run(() => { Console.WriteLine(lazy.Value); }); } Task.WaitAll(tasks); // 所有输出将显示相同的线程ID(因为使用默认 ExecutionAndPublication 模式) }}运行结果将显示所有线程输出相同的初始化信息,证明只有一个线程执行了初始化逻辑。
掌握 Lazy<T> 的线程安全模式 是编写高效、健壮的 .NET多线程编程 应用的关键技能之一。通过合理选择 LazyThreadSafetyMode,你可以在保证线程安全的同时优化性能。
记住:
希望这篇教程能帮助你轻松理解 C#延迟初始化 的核心机制,并在实际项目中灵活运用!
本文由主机测评网于2025-12-11发表在主机测评网_免费VPS_免费云服务器_免费独立服务器,如有疑问,请联系我们。
本文链接:https://www.vpshk.cn/2025126114.html