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


Lusta 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> objektum lusta inicializáláshoz való deklarálásával Orders elkerülheti a rendszererőforrások elsiklását, ha 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 (Shared visual basic) metódusokat biztosít az objektumok lusta inicializálásához osztályterhelés 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 ad át meghatalmazottat, a burkolt típus az értéktulajdonság első elérésekor jön létre Activator.CreateInstance . 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 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)()

Átadhat egy meghatalmazottat 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 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))

A Lazy objektum létrehozása után a lazy változó tulajdonságának Orders első eléréséig Value nem jön létre példány. 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 (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, mint a korábbi, nem példányosít Orders , amíg a tulajdonság első elérése meg Value nem történik.

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 Lazy<T> objektumok tulajdonságához elsőként hozzáférő Value szál inicializálja az összes további hozzáféréshez az összes szálon, és minden szál ugyanazokkal az adatokkal rendelkezik. Ezért nem számít, hogy melyik szál inicializálja az objektumot, és a versenyfeltételek jóindulatúak.

Feljegyzé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 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.

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, adjon meg false egy szerény teljesítménybeli előnyt. Ha több szálból szeretné elérni a tulajdonságot, adja meg true , hogy utasítsa a példányt, hogy megfelelően kezelje a Lazy<T> versenyfeltételeket, amelyekben az egyik szál kivételt jelez az inicializáláskor.

Egyes Lazy<T> konstruktoroknak van egy LazyThreadSafetyMode paraméterük.mode Ezek a konstruktorok további menetbiztonsá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 LazyThreadSafetyModemode paraméter 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 szálbiztos. 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 paraméter megadása LazyThreadSafetyMode.ExecutionAndPublication ugyanaz, mint a isThreadSafe paraméter megadásatrue, a beállítás LazyThreadSafetyMode.None pedig ugyanaz, mint a paraméter megadásafalse.mode

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 példányt Lazy<T> . 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ó: enumerálás LazyThreadSafetyMode .

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 objektum kivétel gyorsítótárazása engedélyezve van, és a tulajdonság első elérésekor kivételt vet ki az Value inicializálási módszerből, ugyanez a kivétel lesz a tulajdonság elérésének Value minden további kísérletekor. 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 engedélyezve van, ha olyan System.Lazy<T> konstruktort használ, amely inicializálási módszert (valueFactory paramétert) használ, például a konstruktor használatakor Lazy(T)(Func(T))engedélyezve van. Ha a konstruktor egy értéket (modeparamétertLazyThreadSafetyMode) is felvesz, adja meg LazyThreadSafetyMode.ExecutionAndPublication vagy LazyThreadSafetyMode.None. Az inicializálási módszer megadása lehetővé teszi a kivétel gyorsítótárazását e két mód esetében. Az inicializálási módszer nagyon egyszerű lehet. Meghívhatja például a paraméter nélküli konstruktort a következőhöz T: new Lazy<Contents>(() => new Contents(), mode) C# vagy New Lazy(Of Contents)(Function() New Contents()) Visual Basic. 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ó: enumerálás LazyThreadSafetyMode .

Feljegyzés

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

Ahogy az előző szakaszban is említettük, Lazy<T> a kivételek eltérő kezelésével LazyThreadSafetyMode.PublicationOnly létrehozott objektumok. Ezzel PublicationOnlytöbb szál is versenyezhet a példány inicializálása Lazy<T> érdekében. Ebben az esetben a kivételek nem lesznek gyorsítótárazva, és a tulajdonság elérésére tett kísérletek mindaddig folytatódhatnak, amíg az Value inicializálás sikeres nem lesz.

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) True (ExecutionAndPublication) vagy false (None) Nem Nem
Lazy(T)(Func(T), logikai) 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, adja meg a tulajdonság háttérmezőjeként Lazy<T>, és adja vissza a Value tulajdonságot a get tulajdonság tartozékából.

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

A Value tulajdonság írásvédett, ezért az azt elérhetővé tevő tulajdonságnak nincs set tartozéka. Ha egy objektum által Lazy<T> támogatott olvasási/írási tulajdonságot igényel, a set kiegészítőnek 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 tulajdonság következő elérése Value az új Lazy<T>inicializálását eredményezi, és Value a tulajdonság ezt követően visszaadja 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 tartozékainak gyorsítótáraznia kell a tulajdonság által Value visszaadott első értéket, és csak a gyorsítótárazott értéket kell módosítania, és ehhez meg kell írnia a saját szálbiztos kódját. 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 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 ThreadLocal<int>(() => 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ásvédett, é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 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 

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

Ha a metódust vagy Parallel.ForEach metódust Parallel.For használja az adatforrások párhuzamos iterálásához, használhatja azokat a túlterheléseket, amelyek beépített támogatást nyújtanak a szálalapú adatokhoz. 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ó : How to: Write a Parallel.For Loop with Thread-Local Variables and How to: Write a Parallel.ForEach Loop with Partition-Local Variables.

Lusta inicializálás használata alacsony terhelésű forgatókönyvekhez

Olyan helyzetekben, amikor nagy számú objektumot kell lusta inicializálnia, dönthet úgy, hogy az egyes objektumok körbefuttatása túl sok memóriát vagy túl sok 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 (Shared Visual Basic) metódusaival lazy-inicializálhatja az System.Threading.LazyInitializer egyes objektumokat anélkül, hogy a példányban Lazy<T>körbefuttatva lenne.

A következő példában tegyük fel, hogy ahelyett, hogy egy Lazy<T> teljes Orders objektumot burkol egy objektumba, akkor csak akkor kell lusta inicializálni az egyes Order objektumokat, ha szükség van rájuk.

// 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 túlterhelést LazyInitializer.EnsureInitialized , amely logikai argumentumot és egy szinkronizálási objektumot vesz igénybe.

Lásd még