Luie initialisatie
Luie initialisatie van een object betekent dat het maken ervan wordt uitgesteld totdat het voor het eerst wordt gebruikt. (Voor dit onderwerp zijn de termen luie initialisatie en luie instantiëring synoniem.) Luie initialisatie wordt voornamelijk gebruikt om de prestaties te verbeteren, verspilling van berekeningen te voorkomen en geheugenvereisten voor programma's te verminderen. Dit zijn de meest voorkomende scenario's:
Wanneer u een object hebt dat duur is om te maken en het programma dit mogelijk niet gebruikt. Stel dat u een object in het geheugen
Customer
hebt met eenOrders
eigenschap die een grote reeksOrder
objecten bevat die, om te worden geïnitialiseerd, een databaseverbinding vereist. Als de gebruiker nooit vraagt om de orders weer te geven of de gegevens in een berekening te gebruiken, is er geen reden om systeemgeheugen of rekencycli te gebruiken om deze te maken.Lazy<Orders>
Door hetOrders
object te declareren voor luie initialisatie, kunt u voorkomen dat systeembronnen worden weggespild wanneer het object niet wordt gebruikt.Wanneer u een object hebt dat duur is om te maken en u het maken ervan wilt uitstellen totdat andere dure bewerkingen zijn voltooid. Stel dat uw programma meerdere objectexemplaren laadt wanneer het wordt gestart, maar slechts enkele exemplaren zijn onmiddellijk vereist. U kunt de opstartprestaties van het programma verbeteren door initialisatie van de objecten uit te stellen die niet vereist zijn totdat de vereiste objecten zijn gemaakt.
Hoewel u uw eigen code kunt schrijven om luie initialisatie uit te voeren, raden we u aan in plaats daarvan te gebruiken Lazy<T> . Lazy<T> en de bijbehorende typen ondersteunen ook thread-veiligheid en bieden een consistent doorgiftebeleid voor uitzonderingen.
De volgende tabel bevat de typen die .NET Framework versie 4 biedt voor het inschakelen van luie initialisatie in verschillende scenario's.
Type | Description |
---|---|
Lazy<T> | Een wrapper-klasse die luie initialisatiesemantiek biedt voor elk klassebibliotheek of door de gebruiker gedefinieerd type. |
ThreadLocal<T> | Lazy<T> Lijkt erop, behalve dat het luie initialisatie-semantiek biedt op basis van thread-local. Elke thread heeft toegang tot een eigen unieke waarde. |
LazyInitializer | Biedt geavanceerde static (Shared in Visual Basic) methoden voor luie initialisatie van objecten zonder overhead van een klasse. |
Eenvoudige luie initialisatie
Als u een luie-geïnitialiseerd type wilt definiëren, bijvoorbeeld MyType
, gebruikt Lazy<MyType>
u (Lazy(Of MyType)
in Visual Basic), zoals wordt weergegeven in het volgende voorbeeld. Als er geen gemachtigde wordt doorgegeven aan de Lazy<T> constructor, wordt het verpakte type gemaakt met behulp van Activator.CreateInstance wanneer de waarde-eigenschap voor het eerst wordt geopend. Als het type geen parameterloze constructor heeft, wordt er een runtime-uitzondering gegenereerd.
In het volgende voorbeeld wordt ervan uitgegaan dat dit Orders
een klasse is die een matrix met Order
objecten bevat die zijn opgehaald uit een database. Een Customer
object bevat een exemplaar van, maar afhankelijk van Orders
gebruikersacties zijn de gegevens van het Orders
object mogelijk niet vereist.
// Initialize by using default Lazy<T> constructor. The
// Orders array itself is not created yet.
Lazy<Orders> _orders = new Lazy<Orders>();
' 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)()
U kunt ook een gemachtigde doorgeven in de Lazy<T> constructor die een specifieke constructoroverbelasting aanroept voor het verpakte type tijdens het maken en andere initialisatiestappen uitvoeren die vereist zijn, zoals wordt weergegeven in het volgende voorbeeld.
// Initialize by invoking a specific constructor on Order when Value
// property is accessed
Lazy<Orders> _orders = new Lazy<Orders>(() => 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))
Nadat het luie object is gemaakt, wordt er geen exemplaar van Orders
gemaakt totdat de Value eigenschap van de luie variabele voor het eerst wordt geopend. Bij eerste toegang wordt het verpakte type gemaakt en geretourneerd en opgeslagen voor toekomstige toegang.
// We need to create the array only if displayOrders is true
if (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
Een Lazy<T> object retourneert altijd hetzelfde object of dezelfde waarde waarmee het is geïnitialiseerd. Daarom is de Value eigenschap alleen-lezen. Als Value u een verwijzingstype opslaat, kunt u er geen nieuw object aan toewijzen. (U kunt echter de waarde van de ingestelde openbare velden en eigenschappen wijzigen.) Als Value een waardetype wordt opgeslagen, kunt u de waarde ervan niet wijzigen. U kunt echter een nieuwe variabele maken door de variabeleconstructor opnieuw aan te roepen met behulp van nieuwe argumenten.
_orders = new Lazy<Orders>(() => new Orders(10));
_orders = New Lazy(Of Orders)(Function() New Orders(10))
De nieuwe luie instantie, zoals de vorige instantie, instantieert pas Orders
wanneer Value de eigenschap voor het eerst wordt geopend.
Thread-veilige initialisatie
Objecten zijn standaard Lazy<T> thread-safe. Als de constructor het soort threadveiligheid niet opgeeft, zijn de Lazy<T> objecten die door de constructor worden gemaakt thread-safe. In scenario's met meerdere threads wordt de eerste thread voor toegang tot de Value eigenschap van een thread-veilig Lazy<T> object geïnitialiseerd voor alle volgende toegang tot alle threads en delen alle threads dezelfde gegevens. Daarom maakt het niet uit welke thread het object initialiseert en rasomstandigheden goedaardig zijn.
Notitie
U kunt deze consistentie uitbreiden naar foutvoorwaarden met uitzonderingscaching. Zie de volgende sectie uitzonderingen in Luie objecten voor meer informatie.
In het volgende voorbeeld ziet u dat hetzelfde Lazy<int>
exemplaar dezelfde waarde heeft voor drie afzonderlijke threads.
// Initialize the integer to the managed thread id of the
// first thread that accesses the Value property.
Lazy<int> number = new Lazy<int>(() => Thread.CurrentThread.ManagedThreadId);
Thread t1 = new Thread(() => Console.WriteLine("number on t1 = {0} ThreadID = {1}",
number.Value, Thread.CurrentThread.ManagedThreadId));
t1.Start();
Thread t2 = new Thread(() => Console.WriteLine("number on t2 = {0} ThreadID = {1}",
number.Value, Thread.CurrentThread.ManagedThreadId));
t2.Start();
Thread t3 = new Thread(() => Console.WriteLine("number on t3 = {0} ThreadID = {1}", number.Value,
Thread.CurrentThread.ManagedThreadId));
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.
Als u afzonderlijke gegevens voor elke thread nodig hebt, gebruikt u het ThreadLocal<T> type, zoals verderop in dit onderwerp wordt beschreven.
Sommige Lazy<T> constructors hebben een Booleaanse parameter die isThreadSafe
wordt gebruikt om op te geven of de Value eigenschap wordt geopend vanuit meerdere threads. Als u van plan bent om toegang te krijgen tot de eigenschap vanaf slechts één thread, geeft u door false
om een bescheiden prestatievoordeel te verkrijgen. Als u van plan bent om toegang te krijgen tot de eigenschap van meerdere threads, geeft true
u door om het Lazy<T> exemplaar te instrueren om racevoorwaarden correct te verwerken waarin één thread een uitzondering genereert tijdens de initialisatie.
Sommige Lazy<T> constructors hebben een parameter met de LazyThreadSafetyMode naam mode
. Deze constructors bieden een extra threadveiligheidsmodus. In de volgende tabel ziet u hoe de threadveiligheid van een Lazy<T> object wordt beïnvloed door constructorparameters die threadveiligheid opgeven. Elke constructor heeft maximaal één dergelijke parameter.
Draadveiligheid van het object | LazyThreadSafetyMode mode parameter |
Booleaanse isThreadSafe parameter |
Geen veiligheidsparameters voor threads |
---|---|---|---|
Volledig thread-veilig; slechts één thread tegelijk probeert de waarde te initialiseren. | ExecutionAndPublication | true |
Ja. |
Niet thread-safe. | None | false |
Niet van toepassing. |
Volledig thread-veilig; threads racen om de waarde te initialiseren. | PublicationOnly | Niet van toepassing. | Niet van toepassing. |
Zoals in de tabel wordt weergegeven, is het opgeven LazyThreadSafetyMode.ExecutionAndPublication voor de mode
parameter hetzelfde als het opgeven true
van de isThreadSafe
parameter en het opgeven hetzelfde als het opgeven LazyThreadSafetyMode.None false
van de parameter.
Zie voor meer informatie over wat Execution
en Publication
raadpleeg LazyThreadSafetyMode.
Als u LazyThreadSafetyMode.PublicationOnly opgeeft, kunnen meerdere threads proberen het Lazy<T> exemplaar te initialiseren. Slechts één thread kan deze race winnen en alle andere threads ontvangen de waarde die is geïnitialiseerd door de geslaagde thread. Als er tijdens de initialisatie een uitzondering op een thread wordt gegenereerd, ontvangt die thread niet de waarde die is ingesteld door de geslaagde thread. Uitzonderingen worden niet in de cache opgeslagen, dus een volgende poging om toegang te krijgen tot de Value eigenschap kan leiden tot een geslaagde initialisatie. Dit verschilt van de manier waarop uitzonderingen worden behandeld in andere modi, die in de volgende sectie worden beschreven. Zie de LazyThreadSafetyMode opsomming voor meer informatie.
Uitzonderingen in luie objecten
Zoals eerder vermeld, retourneert een Lazy<T> object altijd hetzelfde object of dezelfde waarde waarmee het is geïnitialiseerd en is de Value eigenschap daarom alleen-lezen. Als u caching van uitzonderingen inschakelt, wordt deze onveranderbaarheid ook uitgebreid tot uitzonderingsgedrag. Als voor een luie-geïnitialiseerd object uitzonderingscache is ingeschakeld en er een uitzondering wordt gegenereerd van de initialisatiemethode wanneer de eigenschap voor het Value eerst wordt geopend, wordt dezelfde uitzondering gegenereerd bij elke volgende poging om toegang te krijgen tot de Value eigenschap. Met andere woorden, de constructor van het verpakte type wordt nooit opnieuw aangeroepen, zelfs in multithreaded scenario's. Daarom kan het Lazy<T> object geen uitzondering genereren voor één toegang en een waarde retourneren voor een volgende toegang.
Uitzonderingscaching is ingeschakeld wanneer u een System.Lazy<T> constructor gebruikt die een initialisatiemethode (valueFactory
parameter) gebruikt, bijvoorbeeld wanneer u de Lazy(T)(Func(T))
constructor gebruikt. Als de constructor ook een LazyThreadSafetyMode waarde (mode
parameter) gebruikt, geeft u LazyThreadSafetyMode.ExecutionAndPublication op of LazyThreadSafetyMode.None. Als u een initialisatiemethode opgeeft, kunt u uitzonderingen in de cache opslaan voor deze twee modi. De initialisatiemethode kan heel eenvoudig zijn. Het kan bijvoorbeeld de parameterloze constructor aanroepen voor T
: new Lazy<Contents>(() => new Contents(), mode)
in C# of New Lazy(Of Contents)(Function() New Contents())
in Visual Basic. Als u een System.Lazy<T> constructor gebruikt die geen initialisatiemethode opgeeft, worden uitzonderingen die door de parameterloze constructor T
worden gegenereerd, niet in de cache opgeslagen. Zie de LazyThreadSafetyMode opsomming voor meer informatie.
Notitie
Als u een Lazy<T> object maakt waarop de isThreadSafe
constructorparameter is ingesteld of waarop de mode
constructorparameter is ingesteld false
LazyThreadSafetyMode.None, moet u het Lazy<T> object openen vanuit één thread of uw eigen synchronisatie opgeven. Dit geldt voor alle aspecten van het object, inclusief het opslaan van uitzonderingen in de cache.
Zoals vermeld in de vorige sectie, Lazy<T> zijn objecten die zijn gemaakt door uitzonderingen op te geven anders op te LazyThreadSafetyMode.PublicationOnly geven. Met PublicationOnlykunnen meerdere threads concurreren om het Lazy<T> exemplaar te initialiseren. In dit geval worden uitzonderingen niet in de cache opgeslagen en kunnen pogingen om toegang te krijgen tot de Value eigenschap worden voortgezet totdat de initialisatie is geslaagd.
De volgende tabel bevat een overzicht van de manier waarop de Lazy<T> constructors uitzonderingen in cache opslaan beheren.
Constructor | Veiligheidsmodus voor threads | Maakt gebruik van initialisatiemethode | Uitzonderingen worden in de cache opgeslagen |
---|---|---|---|
Luie(T)() | (ExecutionAndPublication) | Nee | Nr. |
Lazy(T)(Func(T)) | (ExecutionAndPublication) | Ja | Ja |
Luie(T)(Booleaanse waarde) | True (ExecutionAndPublication) of false (None) |
Nee | Nr. |
Luie(T)(Func(T), Booleaanse waarde | True (ExecutionAndPublication) of false (None) |
Ja | Ja |
Lazy(T)(LazyThreadSafetyMode) | Door de gebruiker opgegeven | Nee | Nr. |
Lazy(T)(Func(T), LazyThreadSafetyMode) | Door de gebruiker opgegeven | Ja | Nee als de gebruiker opgeeft PublicationOnly; anders, ja. |
Een luie geïnitialiseerde eigenschap implementeren
Als u een openbare eigenschap wilt implementeren met behulp van luie initialisatie, definieert u het backingveld van de eigenschap als een Lazy<T>en retourneert u de Value eigenschap van de get
toegangsfunctie van de eigenschap.
class Customer
{
private 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(this.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
De Value eigenschap heeft het kenmerk Alleen-lezen. Daarom heeft de eigenschap die deze beschikbaar maakt geen set
toegangsrechten. Als u een lees-/schrijfeigenschap nodig hebt die wordt ondersteund door een Lazy<T> object, moet de set
toegangsfunctie een nieuw Lazy<T> object maken en deze toewijzen aan de back-upopslag. De set
accessor moet een lambda-expressie maken die de nieuwe eigenschapswaarde retourneert die is doorgegeven aan de set
accessor en die lambda-expressie doorgeven aan de constructor voor het nieuwe Lazy<T> object. De volgende toegang van de Value eigenschap zorgt voor initialisatie van de nieuwe Lazy<T>, waarna Value de eigenschap daarna de nieuwe waarde retourneert die aan de eigenschap is toegewezen. De reden voor deze convoluted regeling is het behouden van de multithreading bescherming ingebouwd .Lazy<T> Anders moeten de eigenschapstoegangsors de eerste waarde die door de Value eigenschap wordt geretourneerd, in de cache opslaan en alleen de waarde in de cache wijzigen en moet u hiervoor uw eigen threadveilige code schrijven. Vanwege de aanvullende initialisaties die vereist zijn voor een eigenschap lezen/schrijven die wordt ondersteund door een Lazy<T> object, zijn de prestaties mogelijk niet acceptabel. Bovendien kan, afhankelijk van het specifieke scenario, aanvullende coördinatie vereist zijn om racevoorwaarden tussen setters en getters te voorkomen.
Thread-lokale luie initialisatie
In sommige scenario's met meerdere threads wilt u mogelijk de eigen persoonlijke gegevens van elke thread geven. Dergelijke gegevens worden thread-locale gegevens genoemd. In .NET Framework versie 3.5 en eerder kunt u het ThreadStatic
kenmerk toepassen op een statische variabele om deze thread-local te maken. Het gebruik van het ThreadStatic
kenmerk kan echter leiden tot subtiele fouten. Zelfs eenvoudige initialisatie-instructies zorgen er bijvoorbeeld voor dat de variabele alleen wordt geïnitialiseerd op de eerste thread die deze opent, zoals wordt weergegeven in het volgende voorbeeld.
[ThreadStatic]
static int counter = 1;
<ThreadStatic()>
Shared counter As Integer
Op alle andere threads wordt de variabele geïnitialiseerd met behulp van de standaardwaarde (nul). Als alternatief in .NET Framework versie 4 kunt u het System.Threading.ThreadLocal<T> type gebruiken om een op exemplaren gebaseerde, thread-lokale variabele te maken die wordt geïnitialiseerd voor alle threads door de Action<T> gemachtigde die u opgeeft. In het volgende voorbeeld zien alle threads die toegang counter
hebben de beginwaarde als 1.
ThreadLocal<int> betterCounter = new ThreadLocal<int>(() => 1);
Dim betterCounter As ThreadLocal(Of Integer) = New ThreadLocal(Of Integer)(Function() 1)
ThreadLocal<T> verpakt het object op ongeveer dezelfde manier als Lazy<T>, met deze essentiële verschillen:
Elke thread initialiseert de thread-lokale variabele met behulp van zijn eigen persoonlijke gegevens die niet toegankelijk zijn vanuit andere threads.
De ThreadLocal<T>.Value eigenschap is lezen/schrijven en kan elk gewenst aantal keren worden gewijzigd. Dit kan van invloed zijn op de doorgifte van uitzonderingen. Eén bewerking kan bijvoorbeeld een uitzondering genereren,
get
maar de volgende kan de waarde initialiseren.Als er geen initialisatiedelegatie is opgegeven, ThreadLocal<T> wordt het verpakte type geïnitialiseerd met behulp van de standaardwaarde van het type. In dit opzicht ThreadLocal<T> is dit consistent met het ThreadStaticAttribute kenmerk.
In het volgende voorbeeld ziet u dat elke thread die toegang heeft tot het ThreadLocal<int>
exemplaar, een eigen unieke kopie van de gegevens krijgt.
// Initialize the integer to the managed thread id on a per-thread basis.
ThreadLocal<int> threadLocalNumber = new ThreadLocal<int>(() => Thread.CurrentThread.ManagedThreadId);
Thread t4 = new Thread(() => Console.WriteLine("threadLocalNumber on t4 = {0} ThreadID = {1}",
threadLocalNumber.Value, Thread.CurrentThread.ManagedThreadId));
t4.Start();
Thread t5 = new Thread(() => Console.WriteLine("threadLocalNumber on t5 = {0} ThreadID = {1}",
threadLocalNumber.Value, Thread.CurrentThread.ManagedThreadId));
t5.Start();
Thread t6 = new Thread(() => Console.WriteLine("threadLocalNumber on t6 = {0} ThreadID = {1}",
threadLocalNumber.Value, Thread.CurrentThread.ManagedThreadId));
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
Thread-lokale variabelen in Parallel.For en ForEach
Wanneer u de Parallel.For methode of Parallel.ForEach methode gebruikt om gegevensbronnen parallel te herhalen, kunt u de overbelastingen met ingebouwde ondersteuning voor thread-lokale gegevens gebruiken. In deze methoden wordt de thread-localiteit bereikt door lokale gemachtigden te gebruiken om de gegevens te maken, te openen en op te schonen. Zie How to: Write a Parallel.For Loop with Thread-Local Variables and How to: Write a Parallel.ForEach Loop with Partition-Local Variables voor meer informatie.
Luie initialisatie gebruiken voor scenario's met lage overhead
In scenario's waarin u een groot aantal objecten moet lazy-initialiseren, kunt u besluiten dat het verpakken van elk object in een Lazy<T> te veel geheugen of te veel rekenresources vereist. Of misschien hebt u strenge vereisten over hoe luie initialisatie beschikbaar wordt gesteld. In dergelijke gevallen kunt u de static
methoden (Shared
in Visual Basic) van de System.Threading.LazyInitializer klasse gebruiken om elk object lui te initialiseren zonder het in een exemplaar van Lazy<T>.
In het volgende voorbeeld wordt ervan uitgegaan dat u, in plaats van een heel Orders
object in één Lazy<T> object te verpakken, alleen luie geïnitialiseerde afzonderlijke Order
objecten hebt als ze vereist zijn.
// 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
In dit voorbeeld ziet u dat de initialisatieprocedure wordt aangeroepen bij elke iteratie van de lus. In scenario's met meerdere threads is de eerste thread voor het aanroepen van de initialisatieprocedure degene waarvan de waarde door alle threads wordt gezien. Latere threads roepen ook de initialisatieprocedure aan, maar hun resultaten worden niet gebruikt. Als dit soort potentiële racevoorwaarde niet acceptabel is, gebruikt u de overbelasting ervan LazyInitializer.EnsureInitialized met een Booleaanse argument en een synchronisatieobject.