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 Lazy<T> w celu zapewnienia opóźnionego inicjowania z dostępem z wielu wątków.
Nuta
W przykładzie użyto konstruktora Lazy<T>(Func<T>). Demonstruje również użycie konstruktora Lazy<T>(Func<T>, Boolean) (określając true
dla isThreadSafe
) i konstruktora Lazy<T>(Func<T>, LazyThreadSafetyMode) (określając LazyThreadSafetyMode.ExecutionAndPublication dla 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 konstruktor Lazy<T>(Func<T>).
W przykładzie zdefiniowano klasę LargeObject
, która zostanie zainicjowana z opóźnieniem przez jeden z kilku wątków. Cztery kluczowe sekcje kodu ilustrują tworzenie inicjatora, metodę fabryki, rzeczywistą inicjację i konstruktor klasy LargeObject
, która wyświetla komunikat podczas tworzenia obiektu. Na początku metody Main
przykład tworzy bezpieczny wątkowo inicjator leniwy dla 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 factory przedstawia tworzenie obiektu z symbolem zastępczym na potrzeby dalszej inicjalizacji:
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 pierwsze dwie 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 opóźnionego inicjowania. Po naciśnięciu Enter przykład tworzy i uruchamia trzy wątki. Metoda ThreadProc
używana przez wszystkie trzy wątki wywołuje właściwość Value. Przy pierwszym wystąpieniu jest tworzone wystąpienie 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
Konstruktor klasy LargeObject
, która zawiera ostatnią sekcję klucza kodu, wyświetla komunikat i rejestruje tożsamość wątku inicjowania. 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
Nuta
Dla uproszczenia w tym przykładzie użyto globalnego wystąpienia Lazy<T>, a wszystkie metody są static
(Shared
w Visual Basic). Nie są to wymagania dotyczące używania leniwej inicjalizacji.
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 opóźnionego inicjowania, aby odroczyć tworzenie dużego lub intensywnie korzystającego z zasobów obiektu lub wykonanie 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 Lazy<T>. Argument typu tworzonego obiektu Lazy<T> określa typ obiektu, który chcesz zainicjować z opóźnieniem. Konstruktor używany do utworzenia obiektu Lazy<T> określa cechy inicjowania. Inicjowanie z opóźnieniem występuje przy pierwszym uzyskiwaniu dostępu do właściwości Lazy<T>.Value.
W większości przypadków wybór konstruktora zależy od odpowiedzi na dwa pytania:
Czy obiekt zainicjowany z opóźnieniem będzie uzyskiwany z więcej niż jednego wątku? Jeśli tak, obiekt Lazy<T> może utworzyć go w dowolnym wątku. Można użyć jednego z prostych konstruktorów, których domyślne zachowanie polega na utworzeniu obiektu Lazy<T> bezpiecznego wątkowo, tak aby tylko jedno wystąpienie leniwie utworzonego obiektu było tworzone bez względu na to, ile wątków próbuje uzyskać do niego dostęp. Aby utworzyć obiekt Lazy<T>, który nie jest bezpieczny wątkiem, należy użyć konstruktora, który umożliwia określenie bezpieczeństwa wątku.
Ostrożność
Zabezpieczanie wątku obiektu Lazy<T> nie chroni obiektu zainicjowanego z opóźnieniem. Jeśli wiele wątków może uzyskać dostęp do obiektu zainicjowanego z opóźnieniem, należy zapewnić bezpieczeństwo jego właściwości i metod dla dostępu wielowątkowego.
Czy leniwa inicjalizacja wymaga dużo kodu lub czy zainicjowany obiekt z opóźnieniem 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, który konstruktor należy wybrać, na podstawie tych dwóch czynników:
Dostęp do obiektu będzie uzyskiwany przez program | Jeśli nie jest wymagany kod inicjowania (konstruktor bez parametrów), użyj polecenia | Jeśli wymagany jest kod inicjowania, użyj polecenia |
---|---|---|
Wiele wątków | Lazy<T>() | Lazy<T>(Func<T>) |
Jeden wątek |
Lazy<T>(Boolean) z isThreadSafe ustawioną na wartość false . |
Lazy<T>(Func<T>, Boolean) z isThreadSafe ustawioną na wartość 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 obiektu zainicjowanego z opóźnieniem.
buforowanie wyjątków Podczas 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 właściwości Value obiektu Lazy<T>, ten sam wyjątek jest zgłaszany podczas każdej kolejnej próby. Dzięki temu każde wywołanie właściwości Value daje 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.
Lazy<T> oznacza rzeczywiste T
, które w przeciwnym razie zostałyby zainicjowane w pewnym wcześniejszym punkcie, zwykle podczas uruchamiania. Awaria w tym wcześniejszym punkcie jest zwykle śmiertelna. Jeśli istnieje możliwość wystąpienia awarii możliwej do odzyskania, zalecamy utworzenie logiki ponawiania prób w procedurze inicjowania (w tym przypadku metody fabryki), tak jak w przypadku braku leniwego inicjowania.
Alternatywa do blokowania W niektórych sytuacjach warto uniknąć narzutu domyślnego zachowania blokowania obiektu Lazy<T>. W rzadkich sytuacjach może wystąpić 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 Lazy<T> może utworzyć kopię obiektu zainicjowanego z opóźnieniem na każdym z kilku wątków, jeśli wątki wywołają właściwość Value jednocześnie. Obiekt Lazy<T> gwarantuje, że wszystkie wątki używają tego samego wystąpienia obiektu zainicjowanego z opóźnieniem i odrzuca wystąpienia, które nie są używane. W związku z tym koszt zmniejszenia obciążenia związanego 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żny
Po określeniu LazyThreadSafetyMode.PublicationOnlywyjątki nigdy nie są buforowane, nawet jeśli określisz metodę fabryki.
Konstruktory równoważne Oprócz umożliwienia korzystania z LazyThreadSafetyMode.PublicationOnlykonstruktory Lazy<T>(LazyThreadSafetyMode) i Lazy<T>(Func<T>, LazyThreadSafetyMode) mogą duplikować funkcjonalność innych konstruktorów. W poniższej tabeli przedstawiono wartości parametrów, które generują równoważne zachowanie.
Aby utworzyć obiekt Lazy<T>, który jest | W przypadku konstruktorów, które mają parametr LazyThreadSafetyMode mode , ustaw mode na |
W przypadku konstruktorów, które mają parametr isThreadSafe logiczny, ustaw wartość isThreadSafe |
W przypadku konstruktorów 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 można bezpiecznie wątku. | 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żywaniu Lazy<T> z polami statycznymi wątków lub jako magazynem zapasowym właściwości, zobacz Leniwe inicjowanie.
Konstruktory
Lazy<T>() |
Inicjuje nowe wystąpienie klasy Lazy<T>. W przypadku wystąpienia opóźnionego inicjowania używany jest konstruktor bez parametrów typu docelowego. |
Lazy<T>(Boolean) |
Inicjuje nowe wystąpienie klasy Lazy<T>. Gdy wystąpi opóźnienie inicjowania, 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>. Gdy wystąpi opóźnienie inicjowania, używana jest określona funkcja inicjowania. |
Lazy<T>(Func<T>, Boolean) |
Inicjuje nowe wystąpienie klasy Lazy<T>. Gdy wystąpi opóźnienie inicjowania, używana jest określona funkcja inicjowania i tryb inicjowania. |
Lazy<T>(Func<T>, LazyThreadSafetyMode) |
Inicjuje nowe wystąpienie klasy Lazy<T>, która używa określonej funkcji inicjowania i trybu bezpieczeństwa wątków. |
Lazy<T>(LazyThreadSafetyMode) |
Inicjuje nowe wystąpienie klasy Lazy<T>, która używa konstruktora bez parametrów |
Lazy<T>(T) |
Inicjuje nowe wystąpienie klasy Lazy<T>, która używa wstępnie zdefiniowanej wartości. |
Właściwości
IsValueCreated |
Pobiera wartość wskazującą, czy dla tego wystąpienia Lazy<T> utworzono wartość. |
Value |
Pobiera leniwie zainicjowaną wartość bieżącego wystąpienia Lazy<T>. |
Metody
Equals(Object) |
Określa, czy określony obiekt jest równy bieżącemu obiektowi. (Odziedziczone po Object) |
GetHashCode() |
Służy jako domyślna funkcja skrótu. (Odziedziczone po Object) |
GetType() |
Pobiera Type bieżącego wystąpienia. (Odziedziczone po Object) |
MemberwiseClone() |
Tworzy płytkią kopię bieżącego Object. (Odziedziczone po Object) |
ToString() |
Tworzy i zwraca reprezentację ciągu właściwości Value dla tego wystąpienia. |
Dotyczy
Bezpieczeństwo wątkowe
Domyślnie wszystkie publiczne i chronione elementy członkowskie klasy Lazy<T> są bezpieczne wątkowo 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 konstruktorów typu.
Zobacz też
- LazyThreadSafetyMode
- inicjowania z opóźnieniem
-
wyrażeń leniwych (F#)