Uwaga
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Opóźnione inicjowanie obiektu oznacza, że jego tworzenie jest odroczone do momentu pierwszego użycia. (W tym temacie terminy leniwe inicjalizowanie i leniwa inicjalizacja są synonimami). Leniwe inicjalizowanie jest używane głównie w celu zwiększenia wydajności, uniknięcia zbędnych obliczeń i zmniejszenia wymagań pamięciowych programu. Są to najbardziej typowe scenariusze:
Jeśli masz obiekt, który jest kosztowny do utworzenia, a program może go nie używać. Załóżmy na przykład, że w pamięci znajduje się obiekt
Customer
, który ma właściwośćOrders
zawierającą dużą tablicę obiektówOrder
, które, aby zostały zainicjowane, wymagają połączenia z bazą danych. Jeśli użytkownik nigdy nie prosi o wyświetlenie zamówień lub użycie danych w obliczeniach, nie ma powodu używania pamięci systemowej ani cykli obliczeniowych do ich utworzenia. Za pomocą konstrukcjiLazy<Orders>
do zadeklarowania obiektuOrders
dla leniwej inicjalizacji, można uniknąć marnowania zasobów systemowych, gdy obiekt nie jest używany.Jeśli masz obiekt, który jest kosztowny do utworzenia i chcesz odroczyć jego tworzenie do momentu zakończenia innych kosztownych operacji. Załóżmy na przykład, że program ładuje kilka wystąpień obiektów podczas uruchamiania, ale tylko niektóre z nich są wymagane natychmiast. Wydajność uruchamiania programu można poprawić, odroczając inicjowanie obiektów, które nie są wymagane do momentu utworzenia wymaganych obiektów.
Mimo że możesz napisać własny kod do wykonania leniwej inicjalizacji, zalecamy użycie Lazy<T> zamiast tego. Lazy<T> i jego powiązane typy obsługują również bezpieczeństwo wątków i zapewniają jednolitą politykę propagacji wyjątków.
W poniższej tabeli wymieniono typy, które program .NET Framework w wersji 4 udostępnia w celu włączenia inicjowania z opóźnieniem w różnych scenariuszach.
Typ | Opis |
---|---|
Lazy<T> | Klasa otoki, która zapewnia semantykę inicjowania z opóźnieniem dla dowolnej biblioteki klas lub typu zdefiniowanego przez użytkownika. |
ThreadLocal<T> | Lazy<T> Przypomina, z tą różnicą, że zapewnia leniwą semantykę inicjacji na poziomie lokalnym dla wątku. Każdy wątek ma dostęp do własnej unikatowej wartości. |
LazyInitializer | Udostępnia zaawansowane static (Shared w Visual Basic) metody opóźnionego inicjowania obiektów bez nakładu pracy nad klasą. |
Inicjowanie z opóźnieniem podstawowym
Aby zdefiniować typ zainicjowany z opóźnieniem, na przykład MyType
, użyj Lazy<MyType>
(Lazy(Of MyType)
w Visual Basic), jak pokazano w poniższym przykładzie. Jeśli żaden delegat nie zostanie przekazany w konstruktorze Lazy<T> , typ opakowany zostanie utworzony przy użyciu Activator.CreateInstance , gdy właściwość value jest najpierw uzyskiwana. Jeśli typ nie ma konstruktora bez parametrów, zgłaszany jest wyjątek czasu wykonywania.
W poniższym przykładzie przyjęto założenie, że Orders
jest to klasa zawierająca tablicę obiektów pobranych Order
z bazy danych. Obiekt Customer
zawiera wystąpienie Orders
obiektu , ale w zależności od akcji użytkownika dane z Orders
obiektu mogą nie być wymagane.
// Initialize by using default Lazy<T> constructor. The
// Orders array itself is not created yet.
Lazy<Orders> _orders = new();
' Initialize by using default Lazy<T> constructor. The
'Orders array itself is not created yet.
Dim _orders As Lazy(Of Orders) = New Lazy(Of Orders)()
Można również przekazać delegata w konstruktorze Lazy<T> , który wywołuje określone przeciążenie konstruktora na opakowanym typie w czasie tworzenia, i wykonać inne wymagane kroki inicjowania, jak pokazano w poniższym przykładzie.
// Initialize by invoking a specific constructor on Order when Value
// property is accessed
Lazy<Orders> _orders = new(() => new Orders(100));
' Initialize by invoking a specific constructor on Order
' when Value property is accessed
Dim _orders As Lazy(Of Orders) = New Lazy(Of Orders)(Function() New Orders(100))
Po utworzeniu obiektu Lazy nie jest tworzone żadne wystąpienie Orders
, dopóki Value właściwość zmiennej Lazy nie będzie dostępna po raz pierwszy. Po pierwszym dostępie opakowany typ jest tworzony i zwracany oraz przechowywany dla dowolnego przyszłego dostępu.
// We need to create the array only if displayOrders is true
if (s_displayOrders == true)
{
DisplayOrders(_orders.Value.OrderData);
}
else
{
// Don't waste resources getting order data.
}
' We need to create the array only if _displayOrders is true
If _displayOrders = True Then
DisplayOrders(_orders.Value.OrderData)
Else
' Don't waste resources getting order data.
End If
Obiekt Lazy<T> zawsze zwraca ten sam obiekt lub wartość, z którą został zainicjowany. W związku z tym Value właściwość jest tylko do odczytu. Jeśli Value przechowuje typ odwołania, nie można przypisać do niego nowego obiektu. (Można jednak zmienić wartość jego ustawianych pól publicznych i właściwości). Jeśli Value przechowuje typ wartości, nie można zmodyfikować jej wartości. Niemniej jednak można utworzyć nową zmienną, wywołując ponownie konstruktor zmiennej przy użyciu nowych argumentów.
_orders = new Lazy<Orders>(() => new Orders(10));
_orders = New Lazy(Of Orders)(Function() New Orders(10))
Nowe wystąpienie leniwe, takie jak wcześniejsze, nie tworzy swojej instancji Orders
, dopóki jego właściwość Value nie zostanie po raz pierwszy uzyskana.
Inicjowanie bezpieczne wątkowo
Domyślnie Lazy<T> obiekty są bezpieczne dla wątków. Oznacza to, że jeśli konstruktor nie określa rodzaju bezpieczeństwa wątkowego, obiekty Lazy<T> tworzone przez niego są bezpieczne wątkowo. W scenariuszach wielowątkowych, pierwszy wątek, który uzyskuje dostęp do właściwości obiektu bezpiecznego wątkowo Value, inicjuje go dla wszystkich kolejnych dostępów we wszystkich wątkach, a wszystkie wątki współdzielą te same dane. W związku z tym nie ma znaczenia, który wątek inicjuje obiekt, a warunki wyścigu są łagodne.
Uwaga
Tę spójność można rozszerzyć na warunki błędów przy użyciu buforowania wyjątków. Aby uzyskać więcej informacji, zobacz następną sekcję Wyjątki w leniwych obiektach.
W poniższym przykładzie pokazano, że to samo wystąpienie ma tę samą Lazy<int>
wartość dla trzech oddzielnych wątków.
// Initialize the integer to the managed thread id of the
// first thread that accesses the Value property.
Lazy<int> number = new(() => Environment.CurrentManagedThreadId);
Thread t1 = new(() => Console.WriteLine($"number on t1 = {number.Value} ThreadID = {Environment.CurrentManagedThreadId}"));
t1.Start();
Thread t2 = new(() => Console.WriteLine($"number on t2 = {number.Value} ThreadID = {Environment.CurrentManagedThreadId}"));
t2.Start();
Thread t3 = new(() => Console.WriteLine($"number on t3 = {number.Value} ThreadID = {Environment.CurrentManagedThreadId}"));
t3.Start();
// Ensure that thread IDs are not recycled if the
// first thread completes before the last one starts.
t1.Join();
t2.Join();
t3.Join();
/* Sample Output:
number on t1 = 11 ThreadID = 11
number on t3 = 11 ThreadID = 13
number on t2 = 11 ThreadID = 12
Press any key to exit.
*/
' Initialize the integer to the managed thread id of the
' first thread that accesses the Value property.
Dim number As Lazy(Of Integer) = New Lazy(Of Integer)(Function()
Return Thread.CurrentThread.ManagedThreadId
End Function)
Dim t1 As New Thread(Sub()
Console.WriteLine("number on t1 = {0} threadID = {1}",
number.Value, Thread.CurrentThread.ManagedThreadId)
End Sub)
t1.Start()
Dim t2 As New Thread(Sub()
Console.WriteLine("number on t2 = {0} threadID = {1}",
number.Value, Thread.CurrentThread.ManagedThreadId)
End Sub)
t2.Start()
Dim t3 As New Thread(Sub()
Console.WriteLine("number on t3 = {0} threadID = {1}",
number.Value, Thread.CurrentThread.ManagedThreadId)
End Sub)
t3.Start()
' Ensure that thread IDs are not recycled if the
' first thread completes before the last one starts.
t1.Join()
t2.Join()
t3.Join()
' Sample Output:
' number on t1 = 11 ThreadID = 11
' number on t3 = 11 ThreadID = 13
' number on t2 = 11 ThreadID = 12
' Press any key to exit.
Jeśli potrzebujesz oddzielnych danych dla każdego wątku, użyj typu ThreadLocal<T>, zgodnie z opisem w dalszej części tego tematu.
Niektóre Lazy<T> konstruktory mają parametr logiczny o nazwie isThreadSafe
, który służy do określenia, czy Value dostęp do właściwości będzie uzyskiwany z wielu wątków. Jeśli zamierzasz uzyskać dostęp do właściwości tylko z jednego wątku, przekaż wartość false
, aby uzyskać pewną poprawę wydajności. Jeśli zamierzasz uzyskać dostęp do właściwości z wielu wątków, przekaż wartość true
, aby instancja Lazy<T> prawidłowo obsługiwała warunki wyścigu, w których jeden wątek zgłasza wyjątek w czasie inicjalizacji.
Niektóre Lazy<T> konstruktory mają LazyThreadSafetyMode parametr o nazwie mode
. Te konstruktory zapewniają dodatkowy tryb ochrony wątków. W poniższej tabeli pokazano, jak na bezpieczeństwo wątku obiektu Lazy<T> wpływają parametry konstruktora, które określają bezpieczeństwo wątku. Każdy konstruktor ma co najwyżej jeden taki parametr.
Bezpieczeństwo obiektu w kontekście wątku |
LazyThreadSafetyMode
mode parametr |
Parametr logiczny isThreadSafe |
Brak parametrów bezpieczeństwa współbieżności |
---|---|---|---|
W pełni bezpieczne wątkowo; tylko jeden wątek jednocześnie próbuje zainicjować wartość. | ExecutionAndPublication | true |
Tak. |
Nie jest bezpieczny dla wątków. | None | false |
Nie dotyczy. |
W pełni bezpieczny dla wątków; wątki konkurują, aby zainicjować wartość. | PublicationOnly | Nie dotyczy. | Nie dotyczy. |
Jak pokazano w tabeli, określenie LazyThreadSafetyMode.ExecutionAndPublication parametru mode
jest takie samo, jak określenie true
parametru isThreadSafe
, a określenie LazyThreadSafetyMode.None parametru jest takie samo, jak określanie false
parametru .
Aby uzyskać więcej informacji na temat tego, do czego odnoszą się Execution
i Publication
, zobacz LazyThreadSafetyMode.
Określenie LazyThreadSafetyMode.PublicationOnly umożliwia wielu wątkom próbę inicjalizacji wystąpienia Lazy<T>. Tylko jeden wątek może wygrać ten wyścig, a wszystkie pozostałe wątki otrzymują wartość zainicjowaną przez udany wątek. Jeśli w trakcie inicjalizacji jest zgłaszany wyjątek, ten wątek nie otrzymuje wartości ustawionej przez wątek, który zakończył inicjalizację pomyślnie. Wyjątki nie są buforowane, więc kolejna próba uzyskania dostępu Value do właściwości może spowodować pomyślne zainicjowanie. Różni się to od sposobu traktowania wyjątków w innych trybach, które opisano w poniższej sekcji. Aby uzyskać więcej informacji, zobacz LazyThreadSafetyMode enumerację.
Wyjątki w leńiwych obiektach
Jak wspomniano wcześniej, Lazy<T> obiekt zawsze zwraca ten sam obiekt lub wartość, z którą został zainicjowany, a zatem Value właściwość jest tylko do odczytu. Jeśli włączysz buforowanie wyjątków, ta niezmienność obejmuje również zachowanie wyjątków. Jeśli obiekt zainicjowany z opóźnieniem ma włączone buforowanie wyjątków i zgłasza wyjątek z metody inicjalizacji, gdy właściwość Value jest pierwszy raz uzyskiwana, ten sam wyjątek jest zgłaszany przy każdej kolejnej próbie uzyskania dostępu do właściwości Value. Innymi słowy, konstruktor opakowanego typu nigdy nie jest wywoływany ponownie, nawet w scenariuszach wielowątkowych. Lazy<T> W związku z tym obiekt nie może zgłosić wyjątku dla jednego dostępu i zwrócić wartość dla kolejnego dostępu.
Buforowanie wyjątków jest włączone, gdy używasz dowolnego System.Lazy<T> konstruktora, który przyjmuje metodę inicjowania (valueFactory
parametr), na przykład jest on włączony podczas korzystania z konstruktora Lazy(T)(Func(T))
. Jeśli konstruktor przyjmuje również wartość LazyThreadSafetyMode (mode
parametr), określ LazyThreadSafetyMode.ExecutionAndPublication lub LazyThreadSafetyMode.None. Określenie metody inicjowania umożliwia buforowanie wyjątków dla tych dwóch trybów. Metoda inicjowania może być bardzo prosta. Na przykład może wywołać konstruktor bez parametrów dla T
: new Lazy<Contents>(() => new Contents(), mode)
w języku C# lub New Lazy(Of Contents)(Function() New Contents())
w Visual-Basic. Jeśli używasz konstruktora System.Lazy<T> , który nie określa metody inicjowania, wyjątki zgłaszane przez konstruktor bez parametrów nie T
są buforowane. Aby uzyskać więcej informacji, zobacz LazyThreadSafetyMode enumerację.
Uwaga
Jeśli utworzysz Lazy<T> obiekt z parametrem konstruktora ustawionym isThreadSafe
na false
lub mode
parametr konstruktora ustawionym na LazyThreadSafetyMode.None, musisz uzyskać dostęp do Lazy<T> obiektu z pojedynczego wątku lub zapewnić własną synchronizację. Dotyczy to wszystkich aspektów obiektu, w tym buforowania wyjątków.
Jak wspomniano w poprzedniej sekcji, obiekty Lazy<T> utworzone przez specyfikację LazyThreadSafetyMode.PublicationOnly traktują wyjątki inaczej. W programie PublicationOnlywiele wątków może konkurować w celu zainicjowania Lazy<T> wystąpienia. W takim przypadku wyjątki nie są buforowane, a próby uzyskania dostępu do właściwości mogą być kontynuowane do momentu pomyślnego Value zainicjowania.
Poniższa tabela zawiera podsumowanie sposobu Lazy<T> buforowania wyjątków kontrolek konstruktorów.
Konstruktor | Tryb bezpieczeństwa wątków | Używa metody inicjowania | Wyjątki są buforowane |
---|---|---|---|
Leniwy(T)() | (ExecutionAndPublication) | Nie | Nie |
Lazy(T)(Func(T)) | (ExecutionAndPublication) | Tak | Tak |
Lazy(T)(Wartość logiczna) |
True (ExecutionAndPublication) lub false (None) |
Nie | Nie |
Lazy(T)(Func(T), bool) |
True (ExecutionAndPublication) lub false (None) |
Tak | Tak |
Lazy(T)(LazyThreadSafetyMode) | Określony przez użytkownika | Nie | Nie |
Lazy(T)(Func(T), LazyThreadSafetyMode) | Określony przez użytkownika | Tak | Nie, jeśli użytkownik określa PublicationOnly; w przeciwnym razie tak. |
Implementowanie właściwości zainicjowanej z opóźnieniem
Aby zaimplementować właściwość publiczną przy użyciu inicjowania z opóźnieniem Lazy<T>, zdefiniuj pole zapasowe właściwości jako , a następnie zwróć Value właściwość z get
metody dostępu do właściwości .
class Customer
{
private readonly Lazy<Orders> _orders;
public string CustomerID { get; private set; }
public Customer(string id)
{
CustomerID = id;
_orders = new Lazy<Orders>(() =>
{
// You can specify any additional
// initialization steps here.
return new Orders(CustomerID);
});
}
public Orders MyOrders
{
get
{
// Orders is created on first access here.
return _orders.Value;
}
}
}
Class Customer
Private _orders As Lazy(Of Orders)
Public Shared CustomerID As String
Public Sub New(ByVal id As String)
CustomerID = id
_orders = New Lazy(Of Orders)(Function()
' You can specify additional
' initialization steps here
Return New Orders(CustomerID)
End Function)
End Sub
Public ReadOnly Property MyOrders As Orders
Get
Return _orders.Value
End Get
End Property
End Class
Właściwość Value jest tylko do odczytu, dlatego właściwość, która go udostępnia, nie ma akcesora set
. Jeśli potrzebujesz właściwości odczytu/zapisu wspieranej Lazy<T> przez obiekt, set
metoda dostępu musi utworzyć nowy Lazy<T> obiekt i przypisać go do magazynu kopii zapasowych. Metoda set
dostępu musi utworzyć wyrażenie lambda zwracające nową wartość właściwości, która została przekazana do set
metody dostępu, i przekazać to wyrażenie lambda do konstruktora dla nowego Lazy<T> obiektu. Następny dostęp do Value właściwości spowoduje zainicjowanie nowej Lazy<T>właściwości , a jej Value właściwość zwróci nową wartość przypisaną do właściwości . Przyczyną tego zawiłego układu jest zachowanie ochrony wielowątku wbudowanej w Lazy<T>element . W przeciwnym razie metody dostępu do właściwości musiałyby buforować pierwszą wartość zwracaną przez Value właściwość i modyfikować tylko buforowaną wartość, a w tym celu należy napisać własny kod bezpieczny wątkowo. Ze względu na dodatkowe inicjowanie wymagane przez właściwość odczytu/zapisu wspierane przez Lazy<T> obiekt, wydajność może nie być akceptowalna. Ponadto, w zależności od konkretnego scenariusza, może być wymagana dodatkowa koordynacja w celu uniknięcia sytuacji wyścigu między setterami a getterami.
Lokalna dla wątku inicjalizacja z opóźnieniem
W niektórych scenariuszach wielowątkowych warto nadać każdemu wątkowi własne dane prywatne. Takie dane są nazywane danymi lokalnymi wątku. W programie .NET Framework w wersji 3.5 lub starszej można zastosować ThreadStatic
atrybut do zmiennej statycznej, aby utworzyć go w wątku lokalnego. Jednak użycie atrybutu ThreadStatic
może prowadzić do drobnych błędów. Na przykład nawet podstawowe instrukcje inicjowania spowodują zainicjowanie zmiennej tylko w pierwszym wątku, który uzyskuje do niej dostęp, jak pokazano w poniższym przykładzie.
[ThreadStatic]
static int s_counter = 1;
<ThreadStatic()>
Shared counter As Integer
We wszystkich innych wątkach zmienna zostanie zainicjowana przy użyciu jej wartości domyślnej (zero). Alternatywnie w programie .NET Framework w wersji 4 można użyć System.Threading.ThreadLocal<T> typu do utworzenia zmiennej lokalnej opartej na wystąpieniu, która jest inicjowana we wszystkich wątkach przez podanego delegata Action<T> . W poniższym przykładzie wszystkie wątki, do których uzyskuje dostęp counter
, będą widzieć jego wartość początkową jako 1.
ThreadLocal<int> _betterCounter = new(() => 1);
Dim betterCounter As ThreadLocal(Of Integer) = New ThreadLocal(Of Integer)(Function() 1)
ThreadLocal<T> owija swój obiekt w taki sam sposób jak Lazy<T>, z tymi istotnymi różnicami:
Każdy wątek inicjuje zmienną lokalną thread-local przy użyciu własnych danych prywatnych, które nie są dostępne z innych wątków.
Właściwość ThreadLocal<T>.Value jest odczyt-zapis i może być modyfikowana dowolną liczbę razy. Może to mieć wpływ na propagację wyjątku, na przykład jedna
get
operacja może zgłosić wyjątek, ale następna może pomyślnie zainicjować wartość.Jeśli nie podano delegata inicjalizacji, ThreadLocal<T> zainicjuje ten typ opakowany, używając domyślnej wartości dla tego typu. W tym względzie ThreadLocal<T> jest zgodny z atrybutem ThreadStaticAttribute .
W poniższym przykładzie pokazano, że każdy wątek, który uzyskuje ThreadLocal<int>
dostęp do wystąpienia, pobiera własną unikatową kopię danych.
// Initialize the integer to the managed thread id on a per-thread basis.
ThreadLocal<int> threadLocalNumber = new(() => Environment.CurrentManagedThreadId);
Thread t4 = new(() => Console.WriteLine($"threadLocalNumber on t4 = {threadLocalNumber.Value} ThreadID = {Environment.CurrentManagedThreadId}"));
t4.Start();
Thread t5 = new(() => Console.WriteLine($"threadLocalNumber on t5 = {threadLocalNumber.Value} ThreadID = {Environment.CurrentManagedThreadId}"));
t5.Start();
Thread t6 = new(() => Console.WriteLine($"threadLocalNumber on t6 = {threadLocalNumber.Value} ThreadID = {Environment.CurrentManagedThreadId}"));
t6.Start();
// Ensure that thread IDs are not recycled if the
// first thread completes before the last one starts.
t4.Join();
t5.Join();
t6.Join();
/* Sample Output:
threadLocalNumber on t4 = 14 ThreadID = 14
threadLocalNumber on t5 = 15 ThreadID = 15
threadLocalNumber on t6 = 16 ThreadID = 16
*/
' Initialize the integer to the managed thread id on a per-thread basis.
Dim threadLocalNumber As New ThreadLocal(Of Integer)(Function() Thread.CurrentThread.ManagedThreadId)
Dim t4 As New Thread(Sub()
Console.WriteLine("number on t4 = {0} threadID = {1}",
threadLocalNumber.Value, Thread.CurrentThread.ManagedThreadId)
End Sub)
t4.Start()
Dim t5 As New Thread(Sub()
Console.WriteLine("number on t5 = {0} threadID = {1}",
threadLocalNumber.Value, Thread.CurrentThread.ManagedThreadId)
End Sub)
t5.Start()
Dim t6 As New Thread(Sub()
Console.WriteLine("number on t6 = {0} threadID = {1}",
threadLocalNumber.Value, Thread.CurrentThread.ManagedThreadId)
End Sub)
t6.Start()
' Ensure that thread IDs are not recycled if the
' first thread completes before the last one starts.
t4.Join()
t5.Join()
t6.Join()
'Sample(Output)
' threadLocalNumber on t4 = 14 ThreadID = 14
' threadLocalNumber on t5 = 15 ThreadID = 15
' threadLocalNumber on t6 = 16 ThreadID = 16
Zmienne wątkowo-lokalne równolegle.For i ForEach
Gdy używasz metody Parallel.For lub metody Parallel.ForEach do iterowania po źródłach danych równolegle, możesz użyć przeciążeń, które mają wbudowaną obsługę danych lokalnych dla wątku. W tych metodach lokalność wątków jest osiągana przy użyciu delegatów lokalnych do tworzenia, uzyskiwania dostępu i czyszczenia danych. Aby uzyskać więcej informacji, zobacz How to: Write a Parallel.For Loop with Thread-Local Variables (Instrukcje: zapisywanie pętli Parallel.ForEach za pomocą zmiennych lokalnych partycji).
Używanie inicjowania z opóźnieniem dla scenariuszy niskiego obciążenia
W scenariuszach, w których trzeba opóźnić inicjowanie dużej liczby obiektów, można zdecydować, że zawijanie każdego obiektu w obiekcie Lazy<T> wymaga zbyt dużej ilości pamięci lub zbyt wielu zasobów obliczeniowych. Możesz też mieć rygorystyczne wymagania dotyczące sposobu, w jaki jest prezentowana leniwa inicjalizacja. W takich przypadkach można użyć static
metod Shared
(System.Threading.LazyInitializer w Visual Basic) klasy, aby opóźnić inicjowanie każdego obiektu bez zawijania go w wystąpieniu Lazy<T>klasy .
W poniższym przykładzie przyjęto założenie, że zamiast opakowywać cały Orders
obiekt w jednym Lazy<T> obiekcie, pojedyncze obiekty inicjowane Order
są z opóźnieniem tylko wtedy, gdy są wymagane.
// Assume that _orders contains null values, and
// we only need to initialize them if displayOrderInfo is true
if (displayOrderInfo == true)
{
for (int i = 0; i < _orders.Length; i++)
{
// Lazily initialize the orders without wrapping them in a Lazy<T>
LazyInitializer.EnsureInitialized(ref _orders[i], () =>
{
// Returns the value that will be placed in the ref parameter.
return GetOrderForIndex(i);
});
}
}
' Assume that _orders contains null values, and
' we only need to initialize them if displayOrderInfo is true
If displayOrderInfo = True Then
For i As Integer = 0 To _orders.Length
' Lazily initialize the orders without wrapping them in a Lazy(Of T)
LazyInitializer.EnsureInitialized(_orders(i), Function()
' Returns the value that will be placed in the ref parameter.
Return GetOrderForIndex(i)
End Function)
Next
End If
W tym przykładzie zwróć uwagę, że procedura inicjowania jest wywoływana w każdej iteracji pętli. W scenariuszach wielowątkowych pierwszy wątek, który wywołuje procedurę inicjowania, jest tym, którego wartość jest widoczna przez wszystkie wątki. Późniejsze wątki wywołują również procedurę inicjowania, ale ich wyniki nie są używane. Jeśli ten rodzaj potencjalnego stanu wyścigu nie jest akceptowalny, użyj przeciążenia LazyInitializer.EnsureInitialized , które przyjmuje argument logiczny i obiekt synchronizacji.