Megosztás a következőn keresztül:


Késleltetett inicializálás

Az objektum lusta inicializálása azt jelenti, hogy a létrehozása elhalasztva lesz, amíg el nem kezdi használni. (Ebben a témakörben a lusta inicializálás és a lusta példányosítás kifejezés szinonimák.) A lusta inicializálás elsősorban a teljesítmény javítására, a felesleges számítások elkerülésére és a program memóriaigényének csökkentésére szolgál. Ezek a leggyakoribb forgatókönyvek:

  • Ha egy objektum létrehozása költséges, és előfordulhat, hogy a program nem használja. Tegyük fel például, hogy olyan objektum van a memóriában Customer , amely Orders olyan tulajdonságot tartalmaz, amely nagy méretű objektumtömböt Order tartalmaz, és amely inicializálandó, adatbázis-kapcsolatot igényel. Ha a felhasználó soha nem kéri a Rendelések megjelenítését vagy az adatok számításban való használatát, akkor nincs ok arra, hogy rendszermemória vagy számítási ciklusok használatával hozza létre. Az Lazy<Orders> objektumot lusta inicializálás céljából deklarálva Orders, elkerülheti a rendszererőforrások pazarlását, amikor az objektum nincs használatban.

  • Ha olyan objektumot hoz létre, amely költséges, és halasztani szeretné a létrehozását, amíg más költséges műveletek befejeződnek. Tegyük fel például, hogy a program több objektumpéldányt tölt be az indításkor, de ezek közül csak néhányra van szükség azonnal. A program indítási teljesítményét úgy javíthatja, hogy elhalasztja a szükséges objektumok inicializálását, amíg a szükséges objektumok létre nem jönnek.

Bár a lusta inicializáláshoz saját kódot is írhat, javasoljuk, hogy inkább használja Lazy<T> . Lazy<T> és a kapcsolódó típusai támogatják a szálbiztonságot is, és konzisztens kivételterjesztési szabályzatot biztosítanak.

Az alábbi táblázat azokat a típusokat sorolja fel, amelyeket a .NET-keretrendszer 4-es verziója biztosít a lusta inicializálás különböző forgatókönyvekben való engedélyezéséhez.

Típus Leírás
Lazy<T> Burkolóosztály, amely lusta inicializálási szemantikát biztosít bármely osztálytárhoz vagy felhasználó által definiált típushoz.
ThreadLocal<T> Lazy<T> Hasonlít arra a kivételre, hogy lusta inicializálási szemantikát biztosít szál-helyi alapon. Minden szál rendelkezik hozzáféréssel a saját egyedi értékéhez.
LazyInitializer Speciális static metódusokat biztosít az objektumok lusta inicializálásához a Visual Basic-ben (Shared) osztály többletterhelése nélkül.

Alapszintű lusta inicializálás

Lusta inicializált típus MyTypedefiniálásához például használja Lazy<MyType> (Lazy(Of MyType) a Visual Basicben) az alábbi példában látható módon. Ha a Lazy<T> konstruktor nem kap delegáltat, a burkolt típus a value tulajdonság első elérésekor jön létre Activator.CreateInstance segítségével. Ha a típus nem rendelkezik paraméter nélküli konstruktorsal, a rendszer futásidejű kivételt okoz.

Az alábbi példában tegyük fel, hogy Orders egy olyan osztály, amely egy adatbázisból lekért objektumtömböt Order tartalmaz. Az Customer objektumok egy példányt Orderstartalmaznak, de a felhasználói műveletektől függően előfordulhat, hogy az objektum adataira Orders nincs szükség.

// 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)()

Átadhat egy delegáltat is a Lazy<T> konstruktorban, amely egy adott konstruktor túlterhelést hív meg a burkolt típuson a létrehozáskor, és végrehajthat minden szükséges inicializálási lépést, ahogyan az az alábbi példában látható.

// 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))

A Lazy objektum létrehozása után nem jön létre Orders példány, amíg először nem kerül elérésre a Lazy változó Value tulajdonsága. Az első hozzáféréskor a rendszer létrehozza és visszaadja a burkolt típust, és minden későbbi hozzáféréshez tárolja.

// 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

Az Lazy<T> objektumok mindig ugyanazt az objektumot vagy értéket adják vissza, amellyel inicializálták. Ezért a Value tulajdonság írásvédett. Ha Value referenciatípust tárol, nem rendelhet hozzá új objektumot. (Azonban módosíthatja a beállított nyilvános mezők és tulajdonságok értékét.) Ha Value értéktípust tárol, nem módosíthatja az értékét. Ennek ellenére létrehozhat egy új változót a változókonstruktor ismételt meghívásával új argumentumok használatával.

_orders = new Lazy<Orders>(() => new Orders(10));
_orders = New Lazy(Of Orders)(Function() New Orders(10))

Az új lusta példány, akárcsak a korábbi, nem példányosítja a Orders mindaddig, amíg először el nem érik a Value tulajdonságát.

Szálbiztos inicializálás

Alapértelmezés szerint az Lazy<T> objektumok szálbiztosak. Ez azt jelenti, hogy ha a konstruktor nem határozza meg a szál biztonságát, akkor az Lazy<T> általa létrehozott objektumok szálbiztosak. Többszálas forgatókönyvekben a szálbiztos Value objektum Lazy<T> tulajdonságához elsőként hozzáférő szál inicializálja azt minden további hozzáféréshez az összes szálon, és minden szál ugyanazokat az adatokat osztja meg. Ezért nem számít, hogy melyik szál inicializálja az objektumot, és a versenyfeltételek jóindulatúak.

Megjegyzés

Ezt a konzisztenciát kiterjesztheti a hibafeltételekig a kivétel gyorsítótárazásával. További információ: A lazy objektumok kivételei című következő szakasz.

Az alábbi példa azt mutatja be, hogy ugyanaz Lazy<int> a példány ugyanazzal az értékkel rendelkezik három különálló szál esetében.

// 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.

Ha minden szálon külön adatokra van szüksége, használja a ThreadLocal<T> jelen témakör későbbi részében ismertetett típust.

Egyes Lazy<T> konstruktorok rendelkeznek egy logikai paraméterrel, isThreadSafe amely azt határozza meg, hogy a Value tulajdonság több szálból lesz-e elérhető. Ha csak egy szálból szeretné elérni a tulajdonságot, adja meg paraméterként a false értéket egy szerény teljesítménybeli előny eléréséhez. Ha több szálból szeretné elérni a tulajdonságot, adja át true , hogy utasítsa a Lazy<T> példányt a versenyfeltételek helyes kezelésére, amikor az egyik szál kivételt dob az inicializáláskor.

Egyes Lazy<T> konstruktoroknak van egy LazyThreadSafetyMode paraméterük.mode Ezek a konstruktorok további szálbiztonsági módot biztosítanak. Az alábbi táblázat bemutatja, hogyan befolyásolják egy Lazy<T> objektum szálbiztonságát a menetbiztonságot meghatározó konstruktorparaméterek. Minden konstruktornak legfeljebb egy ilyen paramétere van.

Az objektum szálbiztonsága LazyThreadSafetyMode mode paraméter Boolean isThreadSafe logikai paraméter Nincs szálbiztonsági paraméter
Teljesen szálbiztos; egyszerre csak egy szál próbálja inicializálni az értéket. ExecutionAndPublication true Igen.
Nem biztonságos a szálakra nézve. None false Nem alkalmazható.
Teljesen szálbiztos; szálak versenyeznek az érték inicializálásához. PublicationOnly Nem alkalmazható. Nem alkalmazható.

Ahogy a táblázat is mutatja, a LazyThreadSafetyMode.ExecutionAndPublication megadása a mode paraméterhez ugyanaz, mint a true megadása a isThreadSafe paraméterhez, és a LazyThreadSafetyMode.None megadása ugyanaz, mint a false megadása.

További információ arról, hogy mire Execution és Publication mire hivatkozik, lásd LazyThreadSafetyMode: .

A beállítással LazyThreadSafetyMode.PublicationOnly több szál is megpróbálhatja inicializálni a Lazy<T> példányt. Ezt a versenyt csak egy szál tudja megnyerni, a többi szál pedig a sikeres szál által inicializált értéket kapja. Ha az inicializálás során kivétel történik egy szálon, az a szál nem kapja meg a sikeres szál által beállított értéket. A kivételek nem gyorsítótárazva vannak, így a Value tulajdonság elérésére tett későbbi kísérlet sikeres inicializálást eredményezhet. Ez eltér a kivételek más módokon történő kezelésétől, amelyet a következő szakaszban ismertetünk. További információért tekintse meg a LazyThreadSafetyMode felsorolást.

Kivételek a Lusta objektumokban

Ahogy korábban említettem, egy Lazy<T> objektum mindig ugyanazt az objektumot vagy értéket adja vissza, mint amellyel inicializálták, ezért a Value tulajdonság írásvédett. Ha engedélyezi a kivétel gyorsítótárazását, ez a nem módosíthatóság a kivétel viselkedésére is kiterjed. Ha egy lusta inicializált objektumnál engedélyezett a kivétel gyorsítótárazása, és a Value inicializálási módszerből a tulajdonság első elérésekor kivétel keletkezik, akkor ez a kivétel minden további Value tulajdonságelérési kísérletnél újra elő fog fordulni. Más szóval a burkolt típus konstruktorát soha nem hívjuk meg újra, még többszálú forgatókönyvekben sem. Ezért az Lazy<T> objektum nem tud kivételt kivenni egy hozzáférésből, és egy értéket ad vissza egy későbbi hozzáféréshez.

A kivétel gyorsítótárazása akkor van engedélyezve, ha olyan System.Lazy<T> konstruktort használ, amely inicializálási módszert (valueFactory paramétert) alkalmaz; például, amikor a Lazy(T)(Func(T)) konstruktort használja, engedélyezve van. Ha a konstruktor egy LazyThreadSafetyMode értéket (mode paramétert) is felvesz, adja meg a LazyThreadSafetyMode.ExecutionAndPublication vagy a LazyThreadSafetyMode.None értékeket. Az inicializálási módszer megadása lehetővé teszi a kivételek gyorsítótárazását a két üzemmódban. Az inicializálási módszer nagyon egyszerű lehet. Például meghívhatja a paraméter nélküli konstruktort ehhez: T: new Lazy<Contents>(() => new Contents(), mode) C#-ben, vagy New Lazy(Of Contents)(Function() New Contents()) Visual Basic-ben. Ha olyan konstruktort System.Lazy<T> használ, amely nem ad meg inicializálási módszert, a paraméter nélküli konstruktor T által generált kivételek nem lesznek gyorsítótárazva. További információért tekintse meg a LazyThreadSafetyMode felsorolást.

Megjegyzés

Ha egy Lazy<T> objektumot hoz létre, amelynek a isThreadSafe konstruktorparamétere false vagy a mode konstruktorparamétere LazyThreadSafetyMode.None értékre van beállítva, akkor a Lazy<T> objektumot egyetlen szálból kell elérnie, vagy saját szinkronizálást kell biztosítania. Ez az objektum minden aspektusára vonatkozik, beleértve a kivételek gyorsítótárazását is.

Ahogy az előző szakaszban is említettük, a Lazy<T> szerint létrehozott LazyThreadSafetyMode.PublicationOnly objektumok eltérően kezelik a kivételeket. Több szál is versenyezhet az PublicationOnly példány inicializálásáért, amikor Lazy<T> használatos. Ebben az esetben a kivételek nem kerülnek gyorsítótárba, és a Value tulajdonság elérésére tett kísérletek mindaddig folytatódhatnak, amíg az sikeresen inicializálódik.

Az alábbi táblázat összefoglalja, hogy a konstruktorok hogyan szabályozzák a Lazy<T> kivétel gyorsítótárazását.

Konstruktor Menetbiztonsági mód Inicializálási módszert használ A kivételek gyorsítótárazva vannak
Lazy(T)() (ExecutionAndPublication) Nem Nem
Lazy(T)(Func(T)) (ExecutionAndPublication) Igen Igen
Lazy(T)(logikai típus) True (ExecutionAndPublication) vagy false (None) Nem Nem
Lazy(T)(Func(T), bool) True (ExecutionAndPublication) vagy false (None) Igen Igen
Lazy(T)(LazyThreadSafetyMode) Felhasználó által megadott Nem Nem
Lazy(T)(Func(T), LazyThreadSafetyMode) Felhasználó által megadott Igen Nem, ha a felhasználó megadja PublicationOnly; ellenkező esetben igen.

Lazy-Initialized tulajdonság implementálása

Ha lusta inicializálással szeretne megvalósítani egy nyilvános tulajdonságot, definiálja a tulajdonság háttérmezőjét Lazy<T>-ként, és adja vissza a Value tulajdonságot a tulajdonság get hozzáférőjén keresztül.

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

A Value tulajdonság írásvédett, ezért az azt elérhetővé tevő tulajdonságnak nincs set tartozéka. Ha olvasási/írási tulajdonságot igényel, amelyet egy Lazy<T> objektum támogat, a set metódusnak létre kell hoznia egy új Lazy<T> objektumot, és hozzá kell rendelnie a háttértárhoz. A set tartozéknak létre kell hoznia egy lambda kifejezést, amely visszaadja a tartozéknak set átadott új tulajdonságértéket, és átadja a lambda kifejezést az új Lazy<T> objektum konstruktorának. A Value tulajdonság következő elérése az új Lazy<T> inicializálását fogja eredményezni, és ezt követően a Value tulajdonság vissza fogja adni a tulajdonsághoz rendelt új értéket. Ennek a konvolúciós elrendezésnek az az oka, hogy megőrzi a beépített többszálú védelmet Lazy<T>. Ellenkező esetben a tulajdonság elérőinek gyorsítótárazniuk kellene a tulajdonság által Value visszaadott első értéket, és csak a gyorsítótárazott értéket módosítanák, amihez meg kellene írnia a saját szálbiztos kódot. Az objektum által Lazy<T> támogatott olvasási/írási tulajdonság által megkövetelt további inicializálások miatt előfordulhat, hogy a teljesítmény nem elfogadható. Emellett az adott forgatókönyvtől függően további koordinációra lehet szükség a setterek és a getterek közötti versenyfeltételek elkerülése érdekében.

Szál helyi lusta inicializálása

Egyes többszálú forgatókönyvekben érdemes lehet minden szálnak saját privát adatokat adni. Ezeket az adatokat szál-helyi adatoknak nevezzük. A .NET-keretrendszer 3.5-ös és korábbi verziójában az ThreadStatic attribútumot alkalmazhatja egy statikus változóra, hogy az szálalapú legyen. Az attribútum használata ThreadStatic azonban apró hibákhoz vezethet. Például még az alapszintű inicializálási utasítások is csak az első szálon inicializálják a változót, ahogy az az alábbi példában látható.

[ThreadStatic]
static int s_counter = 1;
<ThreadStatic()>
Shared counter As Integer

Az összes többi szálon a változó inicializálása az alapértelmezett érték (nulla) használatával történik. A .NET-keretrendszer 4-es verziójának alternatívaként a típussal System.Threading.ThreadLocal<T> létrehozhat egy példányalapú, szálalapú változót, amelyet az Ön által megadott meghatalmazott inicializál az Action<T> összes szálon. Az alábbi példában az összes hozzáféréssel rendelkező counter szál 1-nek látja a kezdőértéket.

ThreadLocal<int> _betterCounter = new(() => 1);
Dim betterCounter As ThreadLocal(Of Integer) = New ThreadLocal(Of Integer)(Function() 1)

ThreadLocal<T> az objektumot ugyanúgy burkolja, mint Lazy<T>az alábbi alapvető különbségekkel:

  • Minden szál inicializálja a szál helyi változóját a saját személyes adataival, amelyek más szálakból nem érhetők el.

  • A ThreadLocal<T>.Value tulajdonság írás-olvasás, és tetszőleges számú alkalommal módosítható. Ez befolyásolhatja a kivétel propagálását, például egy get művelet kivételt okozhat, a következő azonban sikeresen inicializálhatja az értéket.

  • Ha nincs megadva inicializálási delegált, ThreadLocal<T> inicializálja a burkolt típusát a típus alapértelmezett értékével. Ebben a tekintetben ThreadLocal<T> összhangban van az ThreadStaticAttribute attribútummal.

Az alábbi példa bemutatja, hogy a ThreadLocal<int> példányhoz hozzáférő összes szál saját egyedi másolatot kap az adatokról.

// 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 

Szál-helyi változók a Parallel.For és a ForEach alkalmazásban

Amikor a Parallel.For vagy a Parallel.ForEach metódust használja az adatforrások párhuzamos iterálására, használhatja azokat a túlterhelt verziókat, amelyek beépített támogatást nyújtanak a szálakhoz kötött adatok számára. Ezekben a módszerekben a szál helyének elérése helyi meghatalmazottak használatával történik az adatok létrehozására, elérésére és törlésére. További információkért lásd Hogyan írjunk Parallel.For ciklust szál-lokális változókkal és Hogyan írjunk Parallel.ForEach ciklust partíció-lokális változókkal.

Lusta inicializálás használata alacsony erőforrás-igényű forgatókönyvekhez

Olyan helyzetekben, amikor nagy számú objektumot kell lusta inicializálnia, dönthet úgy, hogy az egyes objektumok becsomagolása túl sok memóriát vagy számítási erőforrást Lazy<T> igényel. Vagy előfordulhat, hogy szigorú követelményei vannak a lusta inicializálás felfedéséről. Ilyen esetekben az static osztály metódusaival (Shared Visual Basic) lusta inicializálással indíthatja az System.Threading.LazyInitializer egyes objektumait anélkül, hogy Lazy<T> példányba csomagolná őket.

A következő példában tegyük fel, hogy ahelyett, hogy egy teljes Orders objektumot egy Lazy<T> objektumba burkolunk, csak az egyes Order objektumokat inicializáljuk lustán, amikor szükséges.

// 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

Ebben a példában figyelje meg, hogy az inicializálási eljárás a ciklus minden iterációján meg lesz hívva. Többszálas forgatókönyvekben az inicializálási eljárás első meghívó szála az, amelynek értékét az összes szál látja. A későbbi szálak is meghívják az inicializálási eljárást, de nem használják az eredményeket. Ha ez a fajta lehetséges versenyfeltétel nem elfogadható, használja a LazyInitializer.EnsureInitialized azon túlterhelt változatát, amely logikai argumentumot és egy szinkronizációs objektumot fogad.

Lásd még