Share via


Aszinkron kifejezések

Ez a cikk az aszinkron kifejezések F#-beli támogatását ismerteti. Az aszinkron kifejezések lehetővé teszik a számítások aszinkron módon történő elvégzését, azaz anélkül, hogy akadályozni lehetne a többi munka végrehajtását. Az aszinkron számítások például olyan alkalmazások írására használhatók, amelyek felhasználói felületekkel rendelkeznek, amelyek az alkalmazás egyéb feladatainak végrehajtásakor is reagálnak a felhasználókra. Az F# aszinkron munkafolyamatok programozási modellje lehetővé teszi funkcionális programok írását, miközben elrejti a száláttűnés részleteit egy tárban.

Az aszinkron kód a .NET-tevékenységeket közvetlenül létrehozó tevékenységkifejezések használatával is létrehozható. A feladatkifejezések használata akkor ajánlott, ha a .NET-feladatokat létrehozó vagy használó .NET-kódtárakkal együttműködik. A legtöbb aszinkron kód F#-ban való írásakor az F# aszinkron kifejezések előnyben részesülnek, mivel tömörebbek, kompozíciósabbak, és elkerülik a .NET-feladatokhoz kapcsolódó bizonyos kifogásokat.

Syntax

async { expression }

Megjegyzések

Az előző szintaxisban az általuk expression képviselt számítás úgy van beállítva, hogy aszinkron módon fusson, azaz az aktuális számítási szál blokkolása nélkül, amikor aszinkron alvási műveleteket, I/O- és egyéb aszinkron műveleteket hajt végre. Az aszinkron számítások gyakran háttérszálon kezdődnek, miközben a végrehajtás az aktuális szálon folytatódik. A kifejezés Async<'T>típusa az, ahol 'T a kulcsszó használatakor a kifejezés return által visszaadott típus található.

Az Async osztály számos forgatókönyvet támogató metódusokat biztosít. Az általános megközelítés az, hogy olyan objektumokat hozunk létre Async , amelyek az aszinkron módon futtatni kívánt számításokat vagy számításokat jelölik, majd ezeket a számításokat az egyik aktiváló függvény használatával indítjuk el. A használt eseményindító attól függ, hogy az aktuális szálat, egy háttérszálat vagy egy .NET-feladatobjektumot szeretne-e használni. Ha például egy aszinkron számítást szeretne elindítani az aktuális szálon, használhatja Async.StartImmediatea következőt: . Amikor elindít egy aszinkron számítást a felhasználói felületi szálról, nem blokkolja a felhasználói műveleteket, például billentyűleütéseket és egértevékenységeket feldolgozó fő eseményhurkot, így az alkalmazás válaszkész marad.

Aszinkron kötés a Let használatával!

Az aszinkron kifejezésekben egyes kifejezések és műveletek szinkronok, mások pedig aszinkronok. Amikor aszinkron módon hív meg egy metódust, a szokásos let kötés let!helyett a . Ennek az a hatása let! , hogy lehetővé teszi a végrehajtás folytatását más számításokon vagy szálakon a számítás végrehajtása során. Miután a let! kötés jobb oldala visszatér, az aszinkron kifejezés többi része folytatja a végrehajtást.

Az alábbi kód a kettő és let!a kettő közötti let különbséget mutatja be. A kódsor, amely csak let egy aszinkron számítást hoz létre objektumként, amelyet később futtathat például Async.StartImmediate vagy Async.RunSynchronously. A számítást használó let! kódsor elindítja a számítást, és aszinkron várakozást hajt végre: a szál fel van függesztve, amíg az eredmény el nem érhető, ekkor a végrehajtás folytatódik.

// let just stores the result as an asynchronous operation.
let (result1 : Async<byte[]>) = stream.AsyncRead(bufferSize)
// let! completes the asynchronous operation and returns the data.
let! (result2 : byte[])  = stream.AsyncRead(bufferSize)

let! csak az F# aszinkron számítások Async<T> közvetlen várására használható. Más típusú aszinkron műveleteket is várhat közvetetten:

  • .NET-feladatok és Task<TResult> nem általánosak Task, a Async.AwaitTask
  • .NET-értékfeladatok és ValueTask<TResult> nem általános ValueTasktevékenységek, .AsTask() a Async.AwaitTask
  • Az F# RFC FS-1097-ben megadott "GetAwaiter" mintát követő objektumok a következővel task { return! expr } |> Async.AwaitTaskkombinálva: .

Átvitelvezérlés

Az aszinkron kifejezések tartalmazhatnak vezérlőfolyamat-szerkezeteket, például for .. in .. do, while .. do, try .. with .., try .. finally .., if .. then .. elseés if .. then ... Ezek azonban további aszinkron szerkezeteket is tartalmazhatnak, kivéve azokat a with kezelőket finally , amelyek szinkron módon hajtanak végre.

Az F# aszinkron kifejezések nem támogatják az aszinkron try .. finally ..kifejezést. Ehhez az esethez használhat tevékenységkifejezést.

use és use! kötések

Az aszinkron kifejezéseken belül a use kötések típusértékekhez IDisposableköthetők. Az utóbbi esetében a rendszer aszinkron módon hajtja végre az ártalmatlanítási tisztítási műveletet.

let!Emellett aszinkron use! kötéseket is végrehajthat. A különbség let!use! és ugyanaz, mint a különbség let és usea . Ehhez use!az objektum az aktuális hatókör végén lesz megsemmisítve. Vegye figyelembe, hogy az F# aktuális kiadásában nem engedélyezi az use! érték null értékűre való inicializálását, annak ellenére use , hogy igen.

Aszinkron primitívek

Egy olyan metódust, amely egyetlen aszinkron feladatot hajt végre, és visszaadja az eredményt, aszinkron primitívnek nevezzük, és ezeket kifejezetten a let!. Az F#-magtárban számos aszinkron primitív definiálva van. A webalkalmazások két ilyen módszere van definiálva a modulban FSharp.Control.WebExtensions: WebRequest.AsyncGetResponse és WebClient.AsyncDownloadString. Mindkét primitív letölt adatokat egy weblapról egy URL-cím alapján. AsyncGetResponse objektumot System.Net.WebResponse hoz létre, és AsyncDownloadString létrehoz egy sztringet, amely egy weblap HTML-jének felel meg.

Az aszinkron I/O-műveletek számos primitív elemét tartalmazza a FSharp.Control.CommonExtensions modul. Az osztály ezen bővítménymetszetei a System.IO.Stream következők Stream.AsyncRead : és Stream.AsyncWrite.

Saját aszinkron primitíveket is írhat egy függvény vagy metódus definiálásával, amelynek törzse aszinkron kifejezés.

Ha más aszinkron modellekhez tervezett Aszinkron metódusokat szeretne használni az F# aszinkron programozási modellel rendelkező .NET-keretrendszer, létre kell hoznia egy függvényt, amely egy F# Async objektumot ad vissza. Az F#-kódtár olyan függvényekkel rendelkezik, amelyek megkönnyítik ezt a feladatot.

Az aszinkron kifejezések használatára itt talál egy példát; Az Async osztály módszereinek dokumentációjában sok más is szerepel.

Ez a példa bemutatja, hogyan lehet aszinkron kifejezéseket használni a kód párhuzamos végrehajtásához.

A következő kód példában egy függvény fetchAsync lekéri egy webes kérelemből visszaadott HTML-szöveget. A fetchAsync függvény aszinkron kódblokkot tartalmaz. Ha egy aszinkron primitív eredményhez kötést hoz létre, ebben az esetben AsyncDownloadStringa rendszer ahelyett használja a kötést let. let!

A függvény Async.RunSynchronously használatával aszinkron műveletet hajthat végre, és megvárhatja az eredményét. Példaként több aszinkron műveletet is végrehajthat párhuzamosan a függvény és a Async.ParallelAsync.RunSynchronously függvény együttes használatával. A Async.Parallel függvény felveszi az Async objektumok listáját, beállítja az egyes Async tevékenységobjektumok kódot, hogy párhuzamosan fussanak, és visszaad egy Async objektumot, amely a párhuzamos számítást képviseli. Csakúgy, mint egyetlen művelet esetében, a végrehajtás indítására kell hívnia Async.RunSynchronously .

A runAll függvény három aszinkron kifejezést indít el párhuzamosan, és megvárja, amíg az összes befejeződött.

open System.Net
open Microsoft.FSharp.Control.WebExtensions

let urlList = [ "Microsoft.com", "http://www.microsoft.com/"
                "MSDN", "http://msdn.microsoft.com/"
                "Bing", "http://www.bing.com"
              ]

let fetchAsync(name, url:string) =
    async {
        try
            let uri = new System.Uri(url)
            let webClient = new WebClient()
            let! html = webClient.AsyncDownloadString(uri)
            printfn "Read %d characters for %s" html.Length name
        with
            | ex -> printfn "%s" (ex.Message);
    }

let runAll() =
    urlList
    |> Seq.map fetchAsync
    |> Async.Parallel
    |> Async.RunSynchronously
    |> ignore

runAll()

Lásd még