Lazy<T> Klasa
Definicja
Ważne
Niektóre informacje odnoszą się do produktu w wersji wstępnej, który może zostać znacząco zmodyfikowany przed wydaniem. Firma Microsoft nie udziela żadnych gwarancji, jawnych lub domniemanych, w odniesieniu do informacji podanych w tym miejscu.
Zapewnia obsługę inicjowania leniwego.
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)
Parametry typu
- T
Typ obiektu, który jest inicjowany leniwie.
- Dziedziczenie
-
Lazy<T>
- Pochodne
- Atrybuty
Przykłady
W poniższym przykładzie pokazano użycie klasy w celu zapewnienia leniwej inicjalizacji Lazy<T> z dostępem z wielu wątków.
Uwaga
W przykładzie użyto konstruktora Lazy<T>(Func<T>) . Demonstruje również użycie Lazy<T>(Func<T>, Boolean) konstruktora (określenie true
dla isThreadSafe
parametru Lazy<T>(Func<T>, LazyThreadSafetyMode) ) i konstruktora (określenie wartości LazyThreadSafetyMode.ExecutionAndPublication mode
). Aby przełączyć się na inny konstruktor, po prostu zmień, które konstruktory są komentowane.
Aby zapoznać się z przykładem buforowania wyjątków przy użyciu tych samych konstruktorów, zobacz Lazy<T>(Func<T>) konstruktor.
W przykładzie zdefiniowano klasę LargeObject
, która zostanie zainicjowana leniwie przez jeden z kilku wątków. Cztery kluczowe sekcje kodu ilustrują tworzenie inicjatora, metodę fabryki, rzeczywistą inicjację i konstruktor LargeObject
klasy, który wyświetla komunikat podczas tworzenia obiektu. Na początku Main
metody przykład tworzy bezpieczny wątkowo inicjator leniwy dla elementu 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)
Metoda fabryki przedstawia tworzenie obiektu z symbolem zastępczym na potrzeby dalszej inicjowania:
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
Należy pamiętać, że dwie pierwsze sekcje kodu można połączyć przy użyciu funkcji lambda, jak pokazano poniżej:
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)
Przykład wstrzymuje się, aby wskazać, że nieokreślony okres może upłynąć przed wystąpieniem inicjowania leniwego. Po naciśnięciu klawisza Enter przykład tworzy i uruchamia trzy wątki. Metoda ThreadProc
używana przez wszystkie trzy wątki wywołuje Value właściwość . Po raz pierwszy LargeObject
zostanie utworzone wystąpienie:
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
Konstruktor LargeObject
klasy, która zawiera ostatnią sekcję klucza kodu, wyświetla komunikat i rejestruje tożsamość wątku inicjującego. Dane wyjściowe z programu są wyświetlane na końcu pełnej listy kodu.
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
Uwaga
Dla uproszczenia w tym przykładzie użyto globalnego wystąpienia klasy Lazy<T>, a wszystkie metody to static
(Shared
w Visual Basic). Nie są wymogi, od których zależy możliwość inicjowania z opóźnieniem.
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
'
Uwagi
Użyj inicjowania z opóźnieniem, aby odroczyć tworzenie dużego lub intensywnie korzystającego z zasobów obiektu lub wykonywanie zadania intensywnie korzystającego z zasobów, szczególnie w przypadku, gdy takie tworzenie lub wykonywanie może nie wystąpić w okresie istnienia programu.
Aby przygotować się do inicjowania z opóźnieniem, należy utworzyć wystąpienie klasy Lazy<T>. Argument typu tworzonego Lazy<T> obiektu określa typ obiektu, który chcesz zainicjować leniwie. Konstruktor, którego używasz do utworzenia Lazy<T> obiektu, określa cechy inicjowania. Inicjowanie z opóźnieniem występuje podczas pierwszego uzyskiwania Lazy<T>.Value dostępu do właściwości.
W większości przypadków wybór konstruktora zależy od odpowiedzi na dwa pytania:
Czy obiekt zainicjowany w sposób leniwy będzie uzyskiwany z więcej niż jednego wątku? Jeśli tak, Lazy<T> obiekt może utworzyć go w dowolnym wątku. Można użyć jednego z prostych konstruktorów, których zachowaniem domyślnym jest utworzenie obiektu bezpiecznego Lazy<T> wątkowo, dzięki czemu tylko jedno wystąpienie lazicznie utworzonego obiektu jest tworzone bez względu na to, ile wątków próbuje uzyskać do niego dostęp. Aby utworzyć Lazy<T> obiekt, który nie jest bezpieczny wątkiem, należy użyć konstruktora, który umożliwia określenie bezpieczeństwa wątku.
Przestroga
Zapewnienie bezpieczeństwa wątku Lazy<T> obiektu nie chroni leniwie zainicjowanego obiektu. Jeśli wiele wątków może uzyskać dostęp do leniwie zainicjowanego obiektu, należy zapewnić bezpieczeństwo jego właściwości i metod w celu uzyskania dostępu wielowątkowego.
Czy leniwa inicjacja wymaga dużo kodu lub czy leniwie zainicjowany obiekt ma konstruktor bez parametrów, który robi wszystko, czego potrzebujesz i nie zgłasza wyjątków? Jeśli musisz napisać kod inicjowania lub jeśli należy obsłużyć wyjątki, użyj jednego z konstruktorów, który przyjmuje metodę fabryki. Napisz kod inicjowania w metodzie factory.
W poniższej tabeli przedstawiono konstruktor do wyboru na podstawie tych dwóch czynników:
Dostęp do obiektu będzie uzyskiwany przez | Jeśli nie jest wymagany kod inicjowania (konstruktor bez parametrów), użyj polecenia | Jeśli kod inicjowania jest wymagany, użyj polecenia |
---|---|---|
Wiele wątków | Lazy<T>() | Lazy<T>(Func<T>) |
Jeden wątek | Lazy<T>(Boolean) z ustawioną wartością isThreadSafe false . |
Lazy<T>(Func<T>, Boolean) z ustawioną wartością isThreadSafe false . |
Aby określić metodę fabryki, można użyć wyrażenia lambda. Spowoduje to zachowanie całego kodu inicjowania w jednym miejscu. Wyrażenie lambda przechwytuje kontekst, w tym wszelkie argumenty przekazywane do konstruktora zainicjowanego obiektu.
Buforowanie wyjątków W przypadku korzystania z metod fabrycznych wyjątki są buforowane. Oznacza to, że jeśli metoda fabryki zgłasza wyjątek przy pierwszym próbie uzyskania dostępu do Value właściwości Lazy<T> obiektu, ten sam wyjątek jest zgłaszany podczas każdej kolejnej próby. Dzięki temu każde wywołanie Value właściwości generuje ten sam wynik i pozwala uniknąć drobnych błędów, które mogą wystąpić, jeśli różne wątki uzyskują różne wyniki. Oznacza Lazy<T> wartość rzeczywistą T
, która w przeciwnym razie zostałaby zainicjowana w pewnym wcześniejszym momencie, zwykle podczas uruchamiania. Awaria w tym wcześniejszym momencie jest zwykle śmiertelna. Jeśli istnieje potencjał możliwego do odzyskania awarii, zalecamy skompilowanie logiki ponawiania prób w procedurze inicjowania (w tym przypadku metoda fabryki), tak jak w przypadku braku leniwego inicjowania.
Alternatywa dla blokady W niektórych sytuacjach warto uniknąć narzutu domyślnego Lazy<T> zachowania blokady obiektu. W rzadkich sytuacjach może istnieć potencjał zakleszczenia. W takich przypadkach można użyć konstruktora Lazy<T>(LazyThreadSafetyMode) lub Lazy<T>(Func<T>, LazyThreadSafetyMode) i określić LazyThreadSafetyMode.PublicationOnly. Dzięki temu obiekt może Lazy<T> utworzyć kopię leniwie zainicjowanego obiektu na każdym z kilku wątków, jeśli wątki wywołają Value właściwość jednocześnie. Obiekt Lazy<T> zapewnia, że wszystkie wątki używają tego samego wystąpienia obiektu zainicjowanego w sposób leniwy i odrzuca wystąpienia, które nie są używane. W związku z tym koszt zmniejszenia obciążenia związane z blokowaniem polega na tym, że program może czasami tworzyć i odrzucać dodatkowe kopie kosztownego obiektu. W większości przypadków jest to mało prawdopodobne. Przykłady konstruktorów Lazy<T>(LazyThreadSafetyMode) i Lazy<T>(Func<T>, LazyThreadSafetyMode) pokazują to zachowanie.
Ważne
Po określeniu LazyThreadSafetyMode.PublicationOnlyparametru wyjątki nigdy nie są buforowane, nawet jeśli określisz metodę fabryki.
Równoważne konstruktory Oprócz włączania używania LazyThreadSafetyMode.PublicationOnlykonstruktorów Lazy<T>(LazyThreadSafetyMode) i Lazy<T>(Func<T>, LazyThreadSafetyMode) mogą zduplikować funkcjonalność innych konstruktorów. W poniższej tabeli przedstawiono wartości parametrów, które generują równoważne zachowanie.
Aby utworzyć Lazy<T> obiekt, który jest | W przypadku konstruktorów, które mają LazyThreadSafetyMode mode parametr, ustaw wartość na mode |
W przypadku konstruktorów, które mają parametr logicznyisThreadSafe , ustaw wartość na isThreadSafe |
Konstruktory bez parametrów bezpieczeństwa wątku |
---|---|---|---|
W pełni bezpieczne wątki; używa blokady, aby upewnić się, że tylko jeden wątek inicjuje wartość. | ExecutionAndPublication | true |
Wszystkie takie konstruktory są w pełni bezpieczne wątkami. |
Nie bezpieczne wątki. | None | false |
Nie dotyczy. |
W pełni bezpieczne wątki; wątki ścigają się, aby zainicjować wartość. | PublicationOnly | Nie dotyczy. | Nie dotyczy. |
Inne możliwości Aby uzyskać informacje o użyciu Lazy<T> z polami statycznymi wątków lub jako magazynem zaplecza dla właściwości, zobacz Inicjowanie z opóźnieniem.
Konstruktory
Lazy<T>() |
Inicjuje nowe wystąpienie klasy Lazy<T>. Gdy wystąpi inicjowanie z opóźnieniem, używany jest konstruktor bez parametrów typu docelowego. |
Lazy<T>(Boolean) |
Inicjuje nowe wystąpienie klasy Lazy<T>. W przypadku wystąpienia inicjowania z opóźnieniem używany jest konstruktor bez parametrów typu docelowego i określony tryb inicjowania. |
Lazy<T>(Func<T>) |
Inicjuje nowe wystąpienie klasy Lazy<T>. W przypadku wystąpienia inicjowania z opóźnieniem jest używana określona funkcja inicjowania. |
Lazy<T>(Func<T>, Boolean) |
Inicjuje nowe wystąpienie klasy Lazy<T>. W przypadku wystąpienia inicjowania z opóźnieniem jest używana określona funkcja inicjowania i tryb inicjowania. |
Lazy<T>(Func<T>, LazyThreadSafetyMode) |
Inicjuje Lazy<T> nowe wystąpienie klasy, które używa określonej funkcji inicjowania i trybu bezpieczeństwa wątków. |
Lazy<T>(LazyThreadSafetyMode) |
Inicjuje Lazy<T> nowe wystąpienie klasy, które używa konstruktora bez parametrów i określonego trybu bezpieczeństwa wątków |
Lazy<T>(T) |
Inicjuje Lazy<T> nowe wystąpienie klasy, które używa wstępnie określonej wartości. |
Właściwości
IsValueCreated |
Pobiera wartość wskazującą, czy dla tego Lazy<T> wystąpienia została utworzona wartość. |
Value |
Pobiera leniwie zainicjowaną wartość bieżącego Lazy<T> wystąpienia. |
Metody
Equals(Object) |
Określa, czy dany obiekt jest taki sam, jak bieżący obiekt. (Odziedziczone po Object) |
GetHashCode() |
Służy jako domyślna funkcja skrótu. (Odziedziczone po Object) |
GetType() |
Type Pobiera wartość bieżącego wystąpienia. (Odziedziczone po Object) |
MemberwiseClone() |
Tworzy płytkią kopię bieżącego Objectelementu . (Odziedziczone po Object) |
ToString() |
Tworzy i zwraca reprezentację Value ciągu właściwości dla tego wystąpienia. |
Dotyczy
Bezpieczeństwo wątkowe
Domyślnie wszystkie publiczne i chronione elementy członkowskie Lazy<T> klasy są bezpieczne wątkami i mogą być używane współbieżnie z wielu wątków. Te gwarancje bezpieczeństwa wątków można usunąć opcjonalnie i na wystąpienie przy użyciu parametrów do konstruktorów typu.