Lazy<T> 类
定义
重要
一些信息与预发行产品相关,相应产品在发行之前可能会进行重大修改。 对于此处提供的信息,Microsoft 不作任何明示或暗示的担保。
提供对延迟初始化的支持。
generic <typename T>
public ref class Lazy
public class Lazy<T>
[System.Runtime.InteropServices.ComVisible(false)]
[System.Serializable]
public class Lazy<T>
type Lazy<'T> = class
[<System.Runtime.InteropServices.ComVisible(false)>]
[<System.Serializable>]
type Lazy<'T> = class
Public Class Lazy(Of T)
类型参数
- T
正在延迟初始化的对象的类型。
- 继承
-
Lazy<T>
- 派生
- 属性
示例
以下示例演示如何使用 Lazy<T> 类提供从多个线程进行访问的延迟初始化。
备注
此示例使用 Lazy<T>(Func<T>) 构造函数。 它还演示了为) 指定的true
构造函数 (用法Lazy<T>(Func<T>, Boolean),以及Lazy<T>(Func<T>, LazyThreadSafetyMode)为mode
) 指定的LazyThreadSafetyMode.ExecutionAndPublication构造函数 (。isThreadSafe
若要切换到其他构造函数,只需更改注释掉哪些构造函数。
有关使用相同构造函数演示异常缓存的示例,请参阅 Lazy<T>(Func<T>) 构造函数。
此示例定义一个将由多个线程之一延迟初始化的 LargeObject
类。 代码的四个关键部分说明了如何创建初始值设定项、工厂方法、实际初始化和类的 LargeObject
构造函数,该构造函数在创建对象时显示消息。 在 Main
方法的开头,该示例为 LargeObject
创建线程安全的延迟初始值:
lazyLargeObject = new Lazy<LargeObject>(InitLargeObject);
// The following lines show how to use other constructors to achieve exactly the
// same result as the previous line:
//lazyLargeObject = new Lazy<LargeObject>(InitLargeObject, true);
//lazyLargeObject = new Lazy<LargeObject>(InitLargeObject,
// LazyThreadSafetyMode.ExecutionAndPublication);
let lazyLargeObject = Lazy<LargeObject> initLargeObject
// The following lines show how to use other constructors to achieve exactly the
// same result as the previous line:
// let lazyLargeObject = Lazy<LargeObject>(initLargeObject, true)
// let lazyLargeObject = Lazy<LargeObject>(initLargeObject,
// LazyThreadSafetyMode.ExecutionAndPublication)
lazyLargeObject = New Lazy(Of LargeObject)(AddressOf InitLargeObject)
' The following lines show how to use other constructors to achieve exactly the
' same result as the previous line:
'lazyLargeObject = New Lazy(Of LargeObject)(AddressOf InitLargeObject, True)
'lazyLargeObject = New Lazy(Of LargeObject)(AddressOf InitLargeObject, _
' LazyThreadSafetyMode.ExecutionAndPublication)
工厂方法显示对象的创建,其中占位符用于进一步初始化:
static LargeObject InitLargeObject()
{
LargeObject large = new LargeObject(Thread.CurrentThread.ManagedThreadId);
// Perform additional initialization here.
return large;
}
let initLargeObject () =
let large = LargeObject Thread.CurrentThread.ManagedThreadId
// Perform additional initialization here.
large
Private Shared Function InitLargeObject() As LargeObject
Dim large As New LargeObject(Thread.CurrentThread.ManagedThreadId)
' Perform additional initialization here.
Return large
End Function
请注意,前两个代码节可以使用 lambda 函数组合,如下所示:
lazyLargeObject = new Lazy<LargeObject>(() =>
{
LargeObject large = new LargeObject(Thread.CurrentThread.ManagedThreadId);
// Perform additional initialization here.
return large;
});
let lazyLargeObject = Lazy<LargeObject>(fun () ->
let large = LargeObject Thread.CurrentThread.ManagedThreadId
// Perform additional initialization here.
large)
lazyLargeObject = New Lazy(Of LargeObject)(Function ()
Dim large As New LargeObject(Thread.CurrentThread.ManagedThreadId)
' Perform additional initialization here.
Return large
End Function)
该示例暂停,以指示在延迟初始化发生之前,不确定期可能已过。 按 Enter 键时,该示例将创建并启动三个线程。 ThreadProc
所有三个线程使用的方法都会调用该Value属性。 首次发生这种情况时,将 LargeObject
创建实例:
LargeObject large = lazyLargeObject.Value;
// IMPORTANT: Lazy initialization is thread-safe, but it doesn't protect the
// object after creation. You must lock the object before accessing it,
// unless the type is thread safe. (LargeObject is not thread safe.)
lock(large)
{
large.Data[0] = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine("Initialized by thread {0}; last used by thread {1}.",
large.InitializedBy, large.Data[0]);
}
let large = lazyLargeObject.Value
// IMPORTANT: Lazy initialization is thread-safe, but it doesn't protect the
// object after creation. You must lock the object before accessing it,
// unless the type is thread safe. (LargeObject is not thread safe.)
lock large (fun () ->
large.Data[0] <- Thread.CurrentThread.ManagedThreadId
printfn $"Initialized by thread {large.InitializedBy} last used by thread {large.Data[0]}.")
Dim large As LargeObject = lazyLargeObject.Value
' IMPORTANT: Lazy initialization is thread-safe, but it doesn't protect the
' object after creation. You must lock the object before accessing it,
' unless the type is thread safe. (LargeObject is not thread safe.)
SyncLock large
large.Data(0) = Thread.CurrentThread.ManagedThreadId
Console.WriteLine("Initialized by thread {0}; last used by thread {1}.", _
large.InitializedBy, large.Data(0))
End SyncLock
类的 LargeObject
构造函数(包括代码的最后一个关键部分)显示消息并记录初始化线程的标识。 程序输出显示在完整代码列表的末尾。
int initBy = 0;
public LargeObject(int initializedBy)
{
initBy = initializedBy;
Console.WriteLine("LargeObject was created on thread id {0}.", initBy);
}
type LargeObject(initBy) =
do
printfn $"LargeObject was created on thread id %i{initBy}."
Private initBy As Integer = 0
Public Sub New(ByVal initializedBy As Integer)
initBy = initializedBy
Console.WriteLine("LargeObject was created on thread id {0}.", initBy)
End Sub
备注
为简单起见,此示例使用 Lazy<T> 的全局实例,所有方法都是 static
(在 Visual Basic 中Shared
)。 使用延迟初始化没有要求。
using System;
using System.Threading;
class Program
{
static Lazy<LargeObject> lazyLargeObject = null;
static LargeObject InitLargeObject()
{
LargeObject large = new LargeObject(Thread.CurrentThread.ManagedThreadId);
// Perform additional initialization here.
return large;
}
static void Main()
{
// The lazy initializer is created here. LargeObject is not created until the
// ThreadProc method executes.
lazyLargeObject = new Lazy<LargeObject>(InitLargeObject);
// The following lines show how to use other constructors to achieve exactly the
// same result as the previous line:
//lazyLargeObject = new Lazy<LargeObject>(InitLargeObject, true);
//lazyLargeObject = new Lazy<LargeObject>(InitLargeObject,
// LazyThreadSafetyMode.ExecutionAndPublication);
Console.WriteLine(
"\r\nLargeObject is not created until you access the Value property of the lazy" +
"\r\ninitializer. Press Enter to create LargeObject.");
Console.ReadLine();
// Create and start 3 threads, each of which uses LargeObject.
Thread[] threads = new Thread[3];
for (int i = 0; i < 3; i++)
{
threads[i] = new Thread(ThreadProc);
threads[i].Start();
}
// Wait for all 3 threads to finish.
foreach (Thread t in threads)
{
t.Join();
}
Console.WriteLine("\r\nPress Enter to end the program");
Console.ReadLine();
}
static void ThreadProc(object state)
{
LargeObject large = lazyLargeObject.Value;
// IMPORTANT: Lazy initialization is thread-safe, but it doesn't protect the
// object after creation. You must lock the object before accessing it,
// unless the type is thread safe. (LargeObject is not thread safe.)
lock(large)
{
large.Data[0] = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine("Initialized by thread {0}; last used by thread {1}.",
large.InitializedBy, large.Data[0]);
}
}
}
class LargeObject
{
public int InitializedBy { get { return initBy; } }
int initBy = 0;
public LargeObject(int initializedBy)
{
initBy = initializedBy;
Console.WriteLine("LargeObject was created on thread id {0}.", initBy);
}
public long[] Data = new long[100000000];
}
/* This example produces output similar to the following:
LargeObject is not created until you access the Value property of the lazy
initializer. Press Enter to create LargeObject.
LargeObject was created on thread id 3.
Initialized by thread 3; last used by thread 3.
Initialized by thread 3; last used by thread 4.
Initialized by thread 3; last used by thread 5.
Press Enter to end the program
*/
open System
open System.Threading
type LargeObject(initBy) =
do
printfn $"LargeObject was created on thread id %i{initBy}."
member _.InitializedBy = initBy
member val Data = Array.zeroCreate<int64> 100000000
let initLargeObject () =
let large = LargeObject Thread.CurrentThread.ManagedThreadId
// Perform additional initialization here.
large
// The lazy initializer is created here. LargeObject is not created until the
// ThreadProc method executes.
let lazyLargeObject = Lazy<LargeObject> initLargeObject
// The following lines show how to use other constructors to achieve exactly the
// same result as the previous line:
// let lazyLargeObject = Lazy<LargeObject>(initLargeObject, true)
// let lazyLargeObject = Lazy<LargeObject>(initLargeObject,
// LazyThreadSafetyMode.ExecutionAndPublication)
let threadProc (state: obj) =
let large = lazyLargeObject.Value
// IMPORTANT: Lazy initialization is thread-safe, but it doesn't protect the
// object after creation. You must lock the object before accessing it,
// unless the type is thread safe. (LargeObject is not thread safe.)
lock large (fun () ->
large.Data[0] <- Thread.CurrentThread.ManagedThreadId
printfn $"Initialized by thread {large.InitializedBy} last used by thread {large.Data[0]}.")
printfn """
LargeObject is not created until you access the Value property of the lazy
initializer. Press Enter to create LargeObject."""
stdin.ReadLine() |> ignore
// Create and start 3 threads, each of which uses LargeObject.
let threads = Array.zeroCreate 3
for i = 0 to 2 do
threads[i] <- Thread(ParameterizedThreadStart threadProc)
threads[i].Start()
// Wait for all 3 threads to finish.
for t in threads do
t.Join()
printfn "\nPress Enter to end the program"
stdin.ReadLine() |> ignore
// This example produces output similar to the following:
// LargeObject is not created until you access the Value property of the lazy
// initializer. Press Enter to create LargeObject.
//
// LargeObject was created on thread id 3.
// Initialized by thread 3 last used by thread 3.
// Initialized by thread 3 last used by thread 4.
// Initialized by thread 3 last used by thread 5.
//
// Press Enter to end the program
Imports System.Threading
Friend Class Program
Private Shared lazyLargeObject As Lazy(Of LargeObject) = Nothing
Private Shared Function InitLargeObject() As LargeObject
Dim large As New LargeObject(Thread.CurrentThread.ManagedThreadId)
' Perform additional initialization here.
Return large
End Function
Shared Sub Main()
' The lazy initializer is created here. LargeObject is not created until the
' ThreadProc method executes.
lazyLargeObject = New Lazy(Of LargeObject)(AddressOf InitLargeObject)
' The following lines show how to use other constructors to achieve exactly the
' same result as the previous line:
'lazyLargeObject = New Lazy(Of LargeObject)(AddressOf InitLargeObject, True)
'lazyLargeObject = New Lazy(Of LargeObject)(AddressOf InitLargeObject, _
' LazyThreadSafetyMode.ExecutionAndPublication)
Console.WriteLine(vbCrLf & _
"LargeObject is not created until you access the Value property of the lazy" _
& vbCrLf & "initializer. Press Enter to create LargeObject.")
Console.ReadLine()
' Create and start 3 threads, each of which uses LargeObject.
Dim threads(2) As Thread
For i As Integer = 0 To 2
threads(i) = New Thread(AddressOf ThreadProc)
threads(i).Start()
Next i
' Wait for all 3 threads to finish.
For Each t As Thread In threads
t.Join()
Next t
Console.WriteLine(vbCrLf & "Press Enter to end the program")
Console.ReadLine()
End Sub
Private Shared Sub ThreadProc(ByVal state As Object)
Dim large As LargeObject = lazyLargeObject.Value
' IMPORTANT: Lazy initialization is thread-safe, but it doesn't protect the
' object after creation. You must lock the object before accessing it,
' unless the type is thread safe. (LargeObject is not thread safe.)
SyncLock large
large.Data(0) = Thread.CurrentThread.ManagedThreadId
Console.WriteLine("Initialized by thread {0}; last used by thread {1}.", _
large.InitializedBy, large.Data(0))
End SyncLock
End Sub
End Class
Friend Class LargeObject
Public ReadOnly Property InitializedBy() As Integer
Get
Return initBy
End Get
End Property
Private initBy As Integer = 0
Public Sub New(ByVal initializedBy As Integer)
initBy = initializedBy
Console.WriteLine("LargeObject was created on thread id {0}.", initBy)
End Sub
Public Data(99999999) As Long
End Class
' This example produces output similar to the following:
'
'LargeObject is not created until you access the Value property of the lazy
'initializer. Press Enter to create LargeObject.
'
'LargeObject was created on thread id 3.
'Initialized by thread 3; last used by thread 3.
'Initialized by thread 3; last used by thread 5.
'Initialized by thread 3; last used by thread 4.
'
'Press Enter to end the program
'
注解
使用延迟初始化延迟创建大型或资源密集型对象,或者延迟执行资源密集型任务,尤其是在程序生存期内可能不会发生此类创建或执行时。
若要准备延迟初始化,请创建一 Lazy<T>个实例。 所创建对象的类型参数 Lazy<T> 指定要初始化延迟的对象的类型。 用于创建对象的 Lazy<T> 构造函数确定初始化的特征。 首次访问 Lazy<T>.Value 属性时出现延迟初始化。
在大多数情况下,选择构造函数取决于两个问题的解答:
是否会从多个线程访问延迟初始化的对象? 如果是这样,该 Lazy<T> 对象可能会在任何线程上创建它。 可以使用其默认行为是创建线程安全 Lazy<T> 对象的简单构造函数之一,因此无论有多少线程尝试访问该对象,都只创建一个延迟实例化对象的实例。 若要创建 Lazy<T> 不是线程安全的对象,必须使用一个构造函数来指定没有线程安全性。
注意
Lazy<T>使对象线程安全不会保护延迟初始化的对象。 如果多个线程可以访问延迟初始化的对象,则必须使其属性和方法安全进行多线程访问。
延迟初始化是否需要大量代码,或者延迟初始化的对象是否具有无参数构造函数,该构造函数会执行所需的一切操作,并且不会引发异常? 如果需要编写初始化代码或需要处理异常,请使用采用工厂方法的构造函数之一。 在工厂方法中编写初始化代码。
下表根据以下两个因素显示了要选择的构造函数:
对象将由 | 如果没有初始化代码 (无参数构造函数) ,请使用 | 如果需要初始化代码,请使用 |
---|---|---|
多个线程 | Lazy<T>() | Lazy<T>(Func<T>) |
一个线程 | Lazy<T>(Boolean),其中 isThreadSafe 设置为 false 。 |
Lazy<T>(Func<T>, Boolean),其中 isThreadSafe 设置为 false 。 |
可以使用 lambda 表达式来指定工厂方法。 这会将所有初始化代码保留在一个位置。 lambda 表达式捕获上下文,包括传递给延迟初始化对象的构造函数的任何参数。
异常缓存 使用工厂方法时,会缓存异常。 也就是说,如果工厂方法首次尝试访问 Value 对象的属性 Lazy<T> 时引发异常,则每次后续尝试时都会引发相同的异常。 这可确保对属性的每个调用 Value 都会生成相同的结果,并避免在不同线程获得不同结果时可能出现的细微错误。 代表 Lazy<T> 实际 T
值,否则在一些早期点(通常在启动期间)初始化。 早点的故障通常是致命的。 如果可能存在可恢复故障,我们建议在本例中将重试逻辑构建到初始化例程 (,工厂方法) ,就像你没有使用延迟初始化一样。
锁定替代方法 在某些情况下,你可能希望避免对象的默认锁定行为的开销 Lazy<T> 。 在极少数情况下,可能存在死锁。 在这种情况下,可以使用 Lazy<T>(LazyThreadSafetyMode) 或 Lazy<T>(Func<T>, LazyThreadSafetyMode) 构造函数并指定 LazyThreadSafetyMode.PublicationOnly。 这样, Lazy<T> 如果线程同时调用 Value 该属性,该对象就可以在多个线程的每个线程上创建延迟初始化对象的副本。 该 Lazy<T> 对象可确保所有线程都使用相同的延迟初始化对象的实例,并丢弃未使用的实例。 因此,降低锁定开销的成本是,程序有时可能会创建和丢弃成本高昂的对象的额外副本。 在大多数情况下,这不太可能。 和构造函数的示例Lazy<T>(LazyThreadSafetyMode)Lazy<T>(Func<T>, LazyThreadSafetyMode)演示了此行为。
重要
指定 LazyThreadSafetyMode.PublicationOnly时,即使指定了工厂方法,也不会缓存异常。
等效构造函数 除了启用使用 LazyThreadSafetyMode.PublicationOnly之外, Lazy<T>(LazyThreadSafetyMode) 构造 Lazy<T>(Func<T>, LazyThreadSafetyMode) 函数还可以复制其他构造函数的功能。 下表显示了生成等效行为的参数值。
创建一个 Lazy<T> 对象 | 对于具有LazyThreadSafetyMode``mode 参数的构造函数,设置为mode |
对于具有布尔isThreadSafe 参数的构造函数,设置为isThreadSafe |
对于没有线程安全参数的构造函数 |
---|---|---|---|
完全线程安全;使用锁定来确保只有一个线程初始化该值。 | ExecutionAndPublication | true |
所有这些构造函数都是完全线程安全的。 |
不是线程安全。 | None | false |
不适用。 |
完全线程安全;线程争用初始化值。 | PublicationOnly | 不适用。 | 不适用。 |
其他功能 有关与线程静态字段一 Lazy<T> 起使用的信息,或作为属性的后盾存储,请参阅 延迟初始化。
构造函数
Lazy<T>() |
初始化 Lazy<T> 类的新实例。 发生迟缓初始化时,使用目标类型的无参数构造函数。 |
Lazy<T>(Boolean) |
初始化 Lazy<T> 类的新实例。 发生迟缓初始化时,使用目标类型的无参数构造函数和指定的初始化模式。 |
Lazy<T>(Func<T>) |
初始化 Lazy<T> 类的新实例。 出现迟缓初始化时,将使用指定的初始化函数。 |
Lazy<T>(Func<T>, Boolean) |
初始化 Lazy<T> 类的新实例。 当延迟初始化发生时,将使用指定的初始化函数和初始化模式。 |
Lazy<T>(Func<T>, LazyThreadSafetyMode) |
初始化 Lazy<T> 类的新实例,它使用指定的初始化函数和线程安全模式。 |
Lazy<T>(LazyThreadSafetyMode) |
初始化 Lazy<T> 类的新实例,其中使用 |
Lazy<T>(T) |
初始化 Lazy<T> 类的新实例,该类使用已预先初始化的指定值。 |
属性
IsValueCreated |
获取一个值,该值表示是否为该 Lazy<T> 实例创建了值。 |
Value |
获取当前 Lazy<T> 实例的延迟初始化值。 |
方法
Equals(Object) |
确定指定对象是否等于当前对象。 (继承自 Object) |
GetHashCode() |
作为默认哈希函数。 (继承自 Object) |
GetType() |
获取当前实例的 Type。 (继承自 Object) |
MemberwiseClone() |
创建当前 Object 的浅表副本。 (继承自 Object) |
ToString() |
创建并返回此实例的 Value 属性的字符串表示形式。 |
适用于
线程安全性
默认情况下,该类的所有公共和受保护成员 Lazy<T> 都是线程安全的,并且可以从多个线程并发使用。 可以使用类型构造函数的参数(可选)和每个实例删除这些线程安全保证。