Aszinkron programozás az Async és az Await használatával (Visual Basic)

Aszinkron programozással elkerülheti a teljesítménybeli szűk keresztmetszeteket, és javíthatja az alkalmazás általános válaszképességét. Az aszinkron alkalmazások írásának hagyományos technikái azonban bonyolultak lehetnek, ami megnehezíti az írást, a hibakeresést és a karbantartást.

A Visual Studio 2012 egyszerűsített megközelítést, aszinkron programozást vezetett be, amely a .NET-keretrendszer 4.5-ös és újabb verzióiban, valamint a Windows Futtatókörnyezetben is aszinkron támogatást nyújt. A fordító elvégzi a fejlesztő által korábban használt nehéz munkát, és az alkalmazás megőrzi a szinkron kódhoz hasonló logikai struktúrát. Ennek eredményeképpen az aszinkron programozás minden előnyét megkapja az erőfeszítés töredékével.

Ez a témakör áttekintést nyújt az aszinkron programozás használatának időpontjáról és módjáról, valamint a részleteket és példákat tartalmazó támogatási témakörökre mutató hivatkozásokat tartalmaz.

Az aszinkron javítja a válaszképességet

Az aszinkronizálás nélkülözhetetlen a potenciálisan blokkoló tevékenységekhez, például amikor az alkalmazás hozzáfér az internethez. A webes erőforrásokhoz való hozzáférés néha lassú vagy késleltetett. Ha egy ilyen tevékenység egy szinkron folyamaton belül le van tiltva, a teljes alkalmazásnak várnia kell. Aszinkron folyamatban az alkalmazás folytathatja más, a webes erőforrástól nem függő munkával, amíg a potenciálisan blokkoló tevékenység be nem fejeződik.

Az alábbi táblázat azokat a tipikus területeket mutatja be, ahol az aszinkron programozás javítja a válaszképességet. A .NET-keretrendszer 4.5-ös és Windows-futtatókörnyezetében felsorolt API-k olyan metódusokat tartalmaznak, amelyek támogatják az aszinkron programozást.

Alkalmazásterület Aszinkron metódusokat tartalmazó támogató API-k
Webes elérés HttpClient, SyndicationClient
Fájlok használata StorageFile, StreamWriter, StreamReader, XmlReader
Képek használata MediaCapture, BitmapEncoder, BitmapDecoder
WCF-programozás Szinkron és aszinkron műveletek

Az aszinkronizálás különösen hasznosnak bizonyul a felhasználói felületi szálhoz hozzáférő alkalmazások számára, mivel a felhasználói felülettel kapcsolatos összes tevékenység általában egy szálon osztozik. Ha egy szinkron alkalmazásban bármilyen folyamat le van tiltva, az összes le lesz tiltva. Az alkalmazás nem válaszol, és arra a következtetésre juthat, hogy sikertelen volt, amikor csak várakozik.

Aszinkron metódusok használatakor az alkalmazás továbbra is válaszol a felhasználói felületre. Átméretezhet vagy kis méretre állíthat egy ablakot, vagy bezárhatja az alkalmazást, ha nem szeretné megvárni, amíg befejeződik.

Az aszinkron megközelítés hozzáadja az automatikus átvitel megfelelőit az aszinkron műveletek tervezésekor választható lehetőségek listájához. Ez azt jelent, hogy a hagyományos aszinkron programozás minden előnyét élvezheti, de sokkal kevesebb erőfeszítést igényel a fejlesztőtől.

Az aszinkron metódusok írása egyszerűbb

A Visual Basic Async és Await kulcsszavai az aszinkron programozás alapjai. A két kulcsszó használatával a .NET-keretrendszerben vagy a Windows Futtatókörnyezetben található erőforrások használatával szinte ugyanolyan egyszerűen hozhat létre aszinkron metódust, mint egy szinkron metódust. Aszinkron metódusok, amelyeket aszinkron metódusok használatával Async definiál, és Await amelyeket aszinkron metódusnak nevezünk.

Az alábbi példa egy aszinkron metódust mutat be. A kód szinte minden elemének teljesen ismerősnek kell lennie. A megjegyzések az aszinkronizálás létrehozásához hozzáadott szolgáltatásokat hívják fel.

A témakör végén egy teljes Windows Presentation Foundation -példafájlt talál, és letöltheti a mintát az Async Sample-ből: Példa az "Aszinkron programozás aszinkron aszinkron és várakozással" című témakörből.

' Three things to note about writing an Async Function:
'  - The function has an Async modifier.
'  - Its return type is Task or Task(Of T). (See "Return Types" section.)
'  - As a matter of convention, its name ends in "Async".
Async Function AccessTheWebAsync() As Task(Of Integer)
    Using client As New HttpClient()
        ' Call and await separately.
        '  - AccessTheWebAsync can do other things while GetStringAsync is also running.
        '  - getStringTask stores the task we get from the call to GetStringAsync.
        '  - Task(Of String) means it is a task which returns a String when it is done.
        Dim getStringTask As Task(Of String) =
            client.GetStringAsync("https://docs.microsoft.com/dotnet")
        ' You can do other work here that doesn't rely on the string from GetStringAsync.
        DoIndependentWork()
        ' The Await operator suspends AccessTheWebAsync.
        '  - AccessTheWebAsync does not continue until getStringTask is complete.
        '  - Meanwhile, control returns to the caller of AccessTheWebAsync.
        '  - Control resumes here when getStringTask is complete.
        '  - The Await operator then retrieves the String result from getStringTask.
        Dim urlContents As String = Await getStringTask
        ' The Return statement specifies an Integer result.
        ' A method which awaits AccessTheWebAsync receives the Length value.
        Return urlContents.Length

    End Using

End Function

Ha AccessTheWebAsync nem rendelkezik olyan munkával, amelyet a hívás GetStringAsync és a befejezésre várna, leegyszerűsítheti a kódot a következő egyetlen utasítás meghívásával és várakozásával.

Dim urlContents As String = Await client.GetStringAsync()

Az alábbi jellemzők összefoglalják, hogy mi teszi az előző példát aszinkron metódussá:

  • A metódus aláírása tartalmaz egy Async módosítót.

  • Az aszinkron metódus neve konvenció szerint "Async" utótaggal végződik.

  • A visszatérési típus az alábbi típusok egyike:

    • Task(Of TResult), ha a metódus olyan visszatérési utasítással rendelkezik, amelyben az operandus TResult típusú.
    • Task ha a metódus nem rendelkezik visszatérési utasítással, vagy operandus nélküli visszatérési utasítással rendelkezik.
    • Al , ha aszinkron eseménykezelőt ír.

    További információt a jelen témakör későbbi, "Visszatérési típusok és paraméterek" című szakaszában talál.

  • A metódus általában tartalmaz legalább egy várakozó kifejezést, amely azt a pontot jelöli, ahol a metódus nem folytatható, amíg a várt aszinkron művelet be nem fejeződik. Addig is a metódus fel lesz függesztve, és a vezérlő visszatér a metódus hívójához. A témakör következő szakasza bemutatja, mi történik a felfüggesztési ponton.

Az aszinkron metódusokban a megadott kulcsszavakat és típusokat használva jelezheti, hogy mit szeretne tenni, a fordító pedig elvégzi a többit, beleértve annak nyomon követését, hogy mi történjen, amikor a vezérlő visszatér egy felfüggesztett metódus várakozási pontjára. Bizonyos rutinfolyamatok, például a hurkok és a kivételkezelés nehézkesen kezelhetők a hagyományos aszinkron kódban. Az aszinkron metódusban ezeket az elemeket ugyanúgy kell megírni, mint egy szinkron megoldásban, és a probléma megoldódott.

A .NET-keretrendszer korábbi verzióiban használt aszinkron működésről további információt a TPL és a hagyományos .NET-keretrendszer aszinkron programozása című témakörben talál.

Mi történik az Async metódusban?

Az aszinkron programozásban az a legfontosabb, hogy a vezérlési folyamat hogyan mozog metódusról metódusra. Az alábbi diagram végigvezeti a folyamaton:

Diagram that shows tracing an async program.

A diagramban szereplő számok a következő lépéseknek felelnek meg:

  1. Az eseménykezelő meghívja és várja az aszinkron AccessTheWebAsync metódust.

  2. AccessTheWebAsync létrehoz egy példányt HttpClient , és meghívja az aszinkron GetStringAsync metódust egy webhely tartalmának sztringként való letöltéséhez.

  3. Valami történik, GetStringAsync ami felfüggeszti a folyamat előrehaladását. Lehet, hogy várnia kell egy webhely letöltésére vagy más blokkoló tevékenységre. Az erőforrások GetStringAsync blokkolásának elkerülése érdekében szabályozza a hívóját. AccessTheWebAsync

    GetStringAsync Visszaad egy tevékenységet (A TResult), ahol a TResult egy sztring, és AccessTheWebAsync hozzárendeli a feladatot a getStringTask változóhoz. A feladat a hívás GetStringAsyncfolyamatban lévő folyamatát jelöli, a munka befejezésekor pedig egy tényleges sztringérték előállítására vonatkozó kötelezettségvállalással.

  4. Mivel getStringTask még nem várták, AccessTheWebAsync folytathatja más olyan munkával, amely nem függ a végeredménytől GetStringAsync. Ezt a munkát a szinkron metódus DoIndependentWorkhívása jelöli.

  5. DoIndependentWork A egy szinkron metódus, amely elvégzi a munkáját, és visszatér a hívóhoz.

  6. AccessTheWebAsync A nem rendelkezik olyan munkával, amelyet eredmény nélkül getStringTaskis elvégezhet. AccessTheWebAsync A Next a letöltött sztring hosszát szeretné kiszámítani és visszaadni, de a metódus addig nem tudja kiszámítani ezt az értéket, amíg a metódus nem tartalmazza a sztringet.

    Ezért egy várakozó operátor használatával függeszti fel a folyamat előrehaladását, AccessTheWebAsync és az irányítást a hívott AccessTheWebAsyncmetódusnak adja vissza. AccessTheWebAsync A visszaadja a Task(Of Integer) hívónak. A feladat azt az ígéretet jelenti, hogy a letöltött sztring hosszát tartalmazó egész számot ad eredményül.

    Megjegyzés

    Ha GetStringAsync (és ezért getStringTask) a várakozás előtt AccessTheWebAsync befejeződött, az irányítás a következőben AccessTheWebAsyncmarad: . Ha az úgynevezett aszinkron folyamat (getStringTask) már befejeződött, és az AccessTheWebSyncnek nem kell megvárnia a végeredményt, a felfüggesztés, majd a visszatérés AccessTheWebAsync költsége kárba vész.

    A hívóban (ebben a példában az eseménykezelőben) a feldolgozási minta folytatódik. Előfordulhat, hogy a hívó más olyan munkát végez, amely nem függ az eredménytől AccessTheWebAsync az eredményre való várakozás előtt, vagy a hívó azonnal várakozhat. Az eseménykezelő a következőre AccessTheWebAsyncvár: AccessTheWebAsyncGetStringAsync.

  7. GetStringAsync A sztringeredményt ad vissza. A sztring eredményét a hívás GetStringAsync nem a várt módon adja vissza. (Ne feledje, hogy a metódus már visszaadott egy feladatot a 3. lépésben.) Ehelyett a sztringeredményt a metódus befejezését jelző feladat tárolja. getStringTask A await operátor a következőből getStringTaskkéri le az eredményt: . A hozzárendelési utasítás hozzárendeli a lekért eredményt a következőhöz urlContents: .

  8. Ha AccessTheWebAsync megvan a sztring eredménye, a metódus kiszámíthatja a sztring hosszát. Ezután a munka AccessTheWebAsync is befejeződött, és a várakozó eseménykezelő folytathatja. A témakör végén található teljes példában ellenőrizheti, hogy az eseménykezelő lekéri és kinyomtatja a hosszeredmény értékét.

Ha még nem ismerkedik az aszinkron programozással, szánjon egy percet a szinkron és az aszinkron viselkedés közötti különbségre. A szinkron metódusok a munka befejezésekor (5. lépés) ad vissza, de az aszinkron metódus feladatértéket ad vissza a munka felfüggesztésekor (3. és 6. lépés). Amikor az aszinkron metódus végül befejezi a munkáját, a tevékenység befejezettként lesz megjelölve, és az eredmény , ha van ilyen, a tevékenységben lesz tárolva.

További információ a vezérlési folyamatról: Control Flow in Async Programs (Visual Basic).

API Async metódusok

Felmerülhet a kérdés, hogy hol találhat olyan módszereket, mint GetStringAsync az aszinkron programozást támogató módszerek. A .NET-keretrendszer 4.5-ös vagy újabb verziója számos olyan tagot tartalmaz, amelyek a AsyncAwait. Ezeket a tagokat az "Async" utótaggal ismerheti fel, amely a tag nevéhez van csatolva, és egy visszatérési Task típussal vagy feladattípussal (TResult). Az osztály például olyan metódusokat tartalmaz, System.IO.Stream mint CopyToAsynca , ReadAsyncés WriteAsync a szinkron metódusok CopyTomellett , Readés Write.

A Windows-futtatókörnyezet számos, a Windows-alkalmazásokban Async használható Await módszert is tartalmaz. További információ és példametalógusok: Aszinkron API-k meghívása C# vagy Visual Basic nyelven, Aszinkron programozás (Windows-futtatókörnyezeti alkalmazások) és WhenAny: Áthidalás a .NET-keretrendszer és a Windows-futtatókörnyezet között.

Szálak

Az aszinkron metódusok nem blokkoló műveletek. Az Await aszinkron metódusban lévő kifejezések nem blokkolják az aktuális szálat, amíg a várt feladat fut. Ehelyett a kifejezés folytatásként regisztrálja a metódus többi részét, és visszaadja a vezérlést az aszinkron metódus hívójának.

A kulcsszavak és Await a Async kulcsszavak nem okoznak további szálakat. Az aszinkron metódusok nem igényelnek többszálú műveletet, mert az aszinkron metódusok nem futnak a saját szálukon. A metódus az aktuális szinkronizálási környezetben fut, és csak akkor használ időt a szálon, ha a metódus aktív. A processzorhoz kötött munkát áthelyezheti Task.Run egy háttérszálba, de a háttérszálak nem segítenek olyan folyamatokban, amelyek csak arra várnak, hogy az eredmények elérhetővé váljanak.

Az aszinkron programozás aszinkron megközelítése szinte minden esetben előnyösebb a meglévő megközelítésekhez. Ez a megközelítés különösen jobb, mint BackgroundWorker az I/O-kötött műveletek esetében, mivel a kód egyszerűbb, és nem kell védekeznie a versenyhelyzetek ellen. Az aszinkron programozással Task.Runkombinálva jobb, mint BackgroundWorker a processzorhoz kötött műveletek esetében, mivel az aszinkron programozás elkülöníti a kód futtatásának koordinációs részleteit a szálkészletbe átviendő munkától Task.Run .

Async és Await

Ha Aszinkron módosító használatával adja meg, hogy a metódus aszinkron metódus, az alábbi két képességet engedélyezi.

  • A megjelölt aszinkron metódus az Await használatával jelölhet ki felfüggesztési pontokat. A await operátor tájékoztatja a fordítót, hogy az aszinkron metódus csak a várt aszinkron folyamat befejezéséig folytathatja a műveletet. Addig is a vezérlő visszatér az aszinkron metódus hívójához.

    Az aszinkron metódus felfüggesztése egy Await kifejezésben nem jelent kilépést a metódusból, és Finally a blokkok nem futnak.

  • A megjelölt aszinkron metódust az azt meghívó metódusok is várhatják.

Az aszinkron metódusok általában egy vagy több operátor előfordulását Await tartalmazzák, de a kifejezések hiánya Await nem okoz fordítóhibát. Ha egy aszinkron metódus nem használ operátort Await a felfüggesztési pont megjelölésére, a metódus szinkron metódusként fut, a Async módosító ellenére. A fordító figyelmeztetést ad ki az ilyen metódusokhoz.

Async és Await környezetfüggő kulcsszavak. További információkért és példákért tekintse meg a következő témaköröket:

Visszaadott típusok és paraméterek

A .NET-keretrendszer programozásban az aszinkron metódus általában egy Task vagy több feladatot (TResult) ad vissza. Az aszinkron metóduson belül a rendszer egy operátort Await alkalmaz egy feladatra, amelyet egy másik aszinkron metódus hívása ad vissza.

A Task(Of TResult) értéket adja meg visszatérési típusként, ha a metódus tartalmaz egy Return utasítást, amely egy operandus típust TResulthatároz meg.

Visszatérési típusként akkor használatos Task , ha a metódus nem rendelkezik visszatérési utasítással, vagy olyan visszatérési utasítással rendelkezik, amely nem ad vissza operandust.

Az alábbi példa bemutatja, hogyan deklarálhat és hívhat meg egy feladatot (TResult) vagy egy Task:

' Signature specifies Task(Of Integer)
Async Function TaskOfTResult_MethodAsync() As Task(Of Integer)

    Dim hours As Integer
    ' . . .
    ' Return statement specifies an integer result.
    Return hours
End Function

' Calls to TaskOfTResult_MethodAsync
Dim returnedTaskTResult As Task(Of Integer) = TaskOfTResult_MethodAsync()
Dim intResult As Integer = Await returnedTaskTResult
' or, in a single statement
Dim intResult As Integer = Await TaskOfTResult_MethodAsync()

' Signature specifies Task
Async Function Task_MethodAsync() As Task

    ' . . .
    ' The method has no return statement.
End Function

' Calls to Task_MethodAsync
Task returnedTask = Task_MethodAsync()
Await returnedTask
' or, in a single statement
Await Task_MethodAsync()

Minden visszaadott feladat a folyamatban lévő munkát jelöli. A feladatok az aszinkron folyamat állapotával kapcsolatos információkat foglalnak össze, és végül a folyamat végeredményét vagy azt a kivételt, amelyet a folyamat jelez, ha nem sikerül.

Az aszinkron metódus is lehet Sub metódus. Ez a visszatérési típus elsősorban eseménykezelők meghatározására szolgál, ahol visszatérési típusra van szükség. Az aszinkron eseménykezelők gyakran kiindulópontként szolgálnak az aszinkron programokhoz.

Az eljárásnak megfelelő aszinkron metódus Sub nem várható meg, és a hívó nem tud kivételeket észlelni, amelyeket a metódus dob.

Az aszinkron metódus nem deklarálhat ByRef-paramétereket , de a metódus meghívhatja az ilyen paraméterekkel rendelkező metódusokat.

További információkért és példákért lásd az Async Return Types (Visual Basic) című témakört. További információ a kivételek aszinkron metódusokban való fogásáról: Try... Fogás... Végül utasítás.

A Windows-futtatókörnyezet programozás aszinkron API-k a következő visszatérési típusok egyikével rendelkeznek, amelyek hasonlóak a tevékenységekhez:

További információ és példa: Aszinkron API-k meghívása C# vagy Visual Basic.

Elnevezési konvenció

Konvenció szerint hozzáfűzi az "Async" kifejezést a módosítóval rendelkező Async metódusok nevéhez.

Figyelmen kívül hagyhatja azt a konvenciót, amelyben egy esemény, alaposztály vagy interfészszerződés más nevet javasol. Például ne nevezze át a gyakori eseménykezelőket, például Button1_Click.

Kapcsolódó témakörök és minták (Visual Studio)

Cím Leírás Sample
Útmutató: Az internet elérése az Async és a Await használatával (Visual Basic) Bemutatja, hogyan alakíthat át szinkron WPF-megoldást aszinkron WPF-megoldássá. Az alkalmazás letölt egy sor webhelyet. Aszinkron minta: Aszinkron programozás Async és Await használatával (Visual Basic)
Útmutató: Az aszinkron bemutató kiterjesztése a Task.WhenAll (Visual Basic) használatával Hozzáadja Task.WhenAll az előző bemutatóhoz. A használat WhenAll az összes letöltést egyszerre indítja el.
Útmutató: Több webes kérés párhuzamos létrehozása az Async és az Await használatával (Visual Basic) Bemutatja, hogyan indíthat el egyszerre több tevékenységet. Aszinkron minta: Több webes kérés párhuzamos létrehozása
Aszinkron visszatérési típusok (Visual Basic) Az aszinkron metódusok által visszaadható típusokat szemlélteti, és elmagyarázza, hogy az egyes típusok mikor megfelelőek.
Aszinkron programok Flow vezérlése (Visual Basic) Részletesen bemutatja a vezérlés folyamatát egy aszinkron programban várakozó kifejezések sorozatán keresztül. Aszinkron minta: Flow vezérlése az aszinkron programokban
Az aszinkron alkalmazás finomhangolása (Visual Basic) A következő funkciók aszinkron megoldáshoz való hozzáadását mutatja be:

- Aszinkron tevékenység vagy tevékenységek listájának visszavonása (Visual Basic)
- Aszinkron tevékenységek megszakítása adott idő elteltével (Visual Basic)
- A hátralévő aszinkron feladatok megszakítása az egyik befejezése után (Visual Basic)
- Több aszinkron feladat elindítása és feldolgozása befejezett állapotban (Visual Basic)
Aszinkron minta: Az alkalmazás finomhangolása
Újraküldés kezelése az Async Appsben (Visual Basic) Bemutatja, hogyan kezelheti azokat az eseteket, amikor egy aktív aszinkron művelet újraindul futás közben.
WhenAny: Áthidalás a .NET-keretrendszer és a Windows-futtatókörnyezet Bemutatja, hogyan hidalhat át a .NET-keretrendszer és az IAsyncOperations tevékenységtípusai között a Windows-futtatókörnyezet, hogy Windows-futtatókörnyezet metódussal is használható WhenAny legyen. Aszinkron minta: Áthidalás a .NET és a Windows-futtatókörnyezet között (AsTask és WhenAny)
Aszinkron lemondás: Áthidalás a .NET-keretrendszer és a Windows-futtatókörnyezet között Bemutatja, hogyan hidalhat át a .NET-keretrendszer és az IAsyncOperations tevékenységtípusai között a Windows-futtatókörnyezet, hogy Windows-futtatókörnyezet metódussal is használható CancellationTokenSource legyen. Aszinkron minta: Áthidalás a .NET és a Windows-futtatókörnyezet között (AsTask-lemondás&)
Az Async használata fájleléréshez (Visual Basic) Felsorolja és bemutatja az aszinkron használat előnyeit, és várja a fájlok elérését.
Feladatalapú aszinkron minta (TAP) Az aszinkronizálás új mintáját ismerteti a .NET-keretrendszer. A minta a TResult és a TaskTask(Of TResult) típuson alapul.
Aszinkron videók a Channel 9-en Az aszinkron programozásról szóló videókra mutató hivatkozásokat tartalmaz.

Példa kitöltése

A következő kód a mainwindow.xaml.vb fájl az Windows megjelenítési alaprendszer (WPF) alkalmazásból, amelyet ez a témakör tárgyal. A mintát letöltheti az Aszinkron mintából: Példa az "Aszinkron programozás Async és Await használatával" című cikkből.


Imports System.Net.Http

' Example that demonstrates Asynchronous Progamming with Async and Await.
' It uses HttpClient.GetStringAsync to download the contents of a website.
' Sample Output:
' Working . . . . . . .
'
' Length of the downloaded string: 39678.

Class MainWindow

    ' Mark the event handler with Async so you can use Await in it.
    Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)

        ' Call and await immediately.
        ' StartButton_Click suspends until AccessTheWebAsync is done.
        Dim contentLength As Integer = Await AccessTheWebAsync()

        ResultsTextBox.Text &= $"{vbCrLf}Length of the downloaded string: {contentLength}.{vbCrLf}"

    End Sub


    ' Three things to note about writing an Async Function:
    '  - The function has an Async modifier. 
    '  - Its return type is Task or Task(Of T). (See "Return Types" section.)
    '  - As a matter of convention, its name ends in "Async".
    Async Function AccessTheWebAsync() As Task(Of Integer)

        Using client As New HttpClient()

            ' Call and await separately. 
            '  - AccessTheWebAsync can do other things while GetStringAsync is also running.
            '  - getStringTask stores the task we get from the call to GetStringAsync. 
            '  - Task(Of String) means it is a task which returns a String when it is done.
            Dim getStringTask As Task(Of String) =
                client.GetStringAsync("https://docs.microsoft.com/dotnet")

            ' You can do other work here that doesn't rely on the string from GetStringAsync. 
            DoIndependentWork()

            ' The Await operator suspends AccessTheWebAsync.
            '  - AccessTheWebAsync does not continue until getStringTask is complete.
            '  - Meanwhile, control returns to the caller of AccessTheWebAsync.
            '  - Control resumes here when getStringTask is complete.
            '  - The Await operator then retrieves the String result from getStringTask.
            Dim urlContents As String = Await getStringTask

            ' The Return statement specifies an Integer result.
            ' A method which awaits AccessTheWebAsync receives the Length value.
            Return urlContents.Length

        End Using

    End Function

    Sub DoIndependentWork()
        ResultsTextBox.Text &= $"Working . . . . . . .{vbCrLf}"
    End Sub

End Class

Lásd még