Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
A .NET-ben a feladatalapú aszinkron minta az új fejlesztéshez javasolt aszinkron tervezési minta. A Task névtérben lévő Task<TResult> és System.Threading.Tasks típusokon alapul, amelyeket aszinkron műveletek megjelenítésére használnak.
Elnevezés, paraméterek és visszatérési típusok
A TAP egyetlen módszert használ az aszinkron művelet indításának és befejezésének ábrázolására. Ez ellentétben áll az aszinkron programozási modell (APM vagy IAsyncResult) mintával és az eseményalapú aszinkron mintával (EAP). Az APM megköveteli a Begin és a End metódusokat. Az EAP olyan metódust igényel, amelynek utótagja Async van, és egy vagy több eseményt, eseménykezelő delegálttípust és EventArg-származtatott típust is igényel. Az aszinkron metódusok a TAP-ban tartalmazzák a Async utótagot a művelet neve után az olyan metódusok esetében, amelyek várható típusokat adnak vissza, például Task, Task<TResult>, ValueTask és ValueTask<TResult>. Egy aszinkron Get művelet, amely egy Task<String> értéket ad vissza, elnevezhető GetAsync. Ha olyan osztályhoz ad hozzá TAP metódust, amely már tartalmaz egy EAP-metódusnevet az Async utótaggal, használja helyette az utótagot TaskAsync . Ha például az osztály már rendelkezik metódussal GetAsync , használja a nevet GetTaskAsync. Ha egy metódus aszinkron műveletet indít el, de nem ad vissza várandós típust, a metódus nevének a következővel Beginkell kezdődnie: , Startvagy valamilyen más ige, amely arra utal, hogy ez a metódus nem adja vissza vagy nem dobja el a művelet eredményét.
A TAP metódus vagy egy System.Threading.Tasks.Task, vagy egy System.Threading.Tasks.Task<TResult> értéket ad vissza, attól függően, hogy a megfelelő szinkron metódus void típussal tér vissza, vagy valamilyen TResult típust ad vissza.
A TAP-metódus paramétereinek egyeznie kell a szinkron megfelelője paramétereivel, és ugyanabban a sorrendben kell megadni. Azonban out és ref paraméterek mentesülnek a szabály alól, ezért teljesen el kell kerülni őket. Az out vagy ref paraméter által visszaadott adatokat inkább a Task<TResult> által visszaadott TResult részeként kell visszaadni, és több érték elhelyezéséhez használjon egy tömböt vagy egy egyéni adatstruktúrát. Érdemes lehet paramétert CancellationToken hozzáadni akkor is, ha a TAP-metódus szinkron megfelelője nem kínál egyet.
A kizárólag a tevékenységek létrehozására, kezelésére vagy kombinációjára fordított metódusoknak (ha a módszer aszinkron szándéka egyértelműen szerepel a metódus nevében vagy annak a típusnak a nevében, amelyhez a metódus tartozik) nem kell követni ezt az elnevezési mintát; ezeket a módszereket gyakran kombinátoroknak nevezik. Példák a kombinátorokra: WhenAll és WhenAny, amelyekről a Beépített feladatalapú kombinátorok használata című szakasz tárgyalja a Feladatalapú aszinkron minta fogyasztása című cikkben.
Ha például a TAP szintaxis eltér az örökölt aszinkron programozási mintákban, például az Aszinkron programozási modellben (APM) és az eseményalapú aszinkron mintában (EAP) használt szintaxistól, tekintse meg az aszinkron programozási mintákat.
Aszinkron művelet kezdeményezése
A TAP-on alapuló aszinkron metódusok kis mennyiségű munkát végezhetnek szinkron módon, például az argumentumok érvényesítése és az aszinkron művelet kezdeményezése, mielőtt visszaadják az eredményül kapott feladatot. A szinkron munkát a minimálisra kell csökkenteni, hogy az aszinkron metódus gyorsan visszatérjen. A gyors visszatérés okai a következők:
Aszinkron metódusok hívhatók meg felhasználói felületi (UI-) szálakból, és a hosszan futó szinkron munka károsíthatja az alkalmazás válaszképességét.
Egyszerre több aszinkron metódus is elindítható. Ezért az aszinkron módszer szinkron részében végzett minden hosszú ideig futó munka késleltetheti az egyéb aszinkron műveletek indítását, ezáltal csökkentve az egyidejűség előnyeit.
Bizonyos esetekben a művelet végrehajtásához szükséges munka mennyisége kisebb, mint a művelet aszinkron elindításához szükséges munka mennyisége. Ilyen forgatókönyv például egy olyan adatfolyamból való olvasás, amelyben az olvasási műveletet a memóriában már pufferelt adatokkal lehet kielégíteni. Ilyen esetekben a művelet szinkron módon fejeződhet be, és egy már befejezett feladatot is visszaadhat.
Kivételek
Az aszinkron metódusnak kivételt kell dobnia az aszinkron metódushívás elhagyására, csak használati hibára válaszul. A használati hibák soha nem fordulhatnak elő az éles kódban. Ha például egy metódus egyik argumentumaként nullhivatkozást ad át (Nothing a Visual Basic-ben), és ez hibás állapotot okoz (amit általában egy ArgumentNullException kivétel jelez), módosíthatja a hívó kódot, hogy a nullhivatkozás soha ne legyen átadva. Minden egyéb hiba esetén az aszinkron metódus futtatásakor előforduló kivételeket hozzá kell rendelni a visszaadott tevékenységhez, még akkor is, ha az aszinkron metódus szinkron módon fejeződik be a feladat visszaadása előtt. A tevékenységek általában legfeljebb egy kivételt tartalmaznak. Ha azonban a tevékenység több műveletet jelöl (például WhenAll), több kivétel is társítható egyetlen tevékenységhez.
Célkörnyezet
A TAP metódus implementálásakor meghatározhatja, hogy hol történik az aszinkron végrehajtás. Dönthet úgy, hogy végrehajtja a számítási feladatot a szálkészleten, aszinkron I/O használatával valósítja meg (anélkül, hogy a művelet végrehajtásához egy szálhoz lenne kötve), futtathatja egy adott szálon (például a felhasználói felületen), vagy bármilyen számú lehetséges környezetet használhat. Előfordulhat, hogy a TAP-metódusnak még nincs mit végrehajtania, és csak egy Task olyan feltételt adhat vissza, amely a rendszer más részén található állapot előfordulását jelzi (például egy olyan feladatot, amely egy várólistára helyezett adatstruktúrába érkező adatokat jelöl).
A TAP metódus hívója letilthatja a TAP metódus befejezésére való várakozást az eredményül kapott tevékenységre való szinkron várakozással, vagy további (folytatási) kódot futtathat az aszinkron művelet befejezésekor. A folytatási kód létrehozója szabályozza a kód végrehajtásának helyét. A folytatási kódot explicit módon, az Task osztály metódusaival (például ContinueWith) vagy implicit módon hozhatja létre a folytatásokra épülő nyelvi támogatással (például C#- await ban, Await Visual Basicben, AwaitValue F#-ban).
Tevékenység állapota
Az Task osztály egy életciklust biztosít az aszinkron műveletekhez, és ezt a ciklust az TaskStatus enumerálás képviseli. Az Task és Task<TResult> típusokból származó szélsőséges esetek támogatása érdekében, valamint az építés ütemezéstől való elválasztás támogatására a Task osztály egy Start metódust tesz elérhetővé. A nyilvános Task konstruktorok által létrehozott tevékenységeket hideg tevékenységeknek nevezzük, mivel nem ütemezett Created állapotban kezdik meg az életciklusukat, és csak akkor ütemezettek, amikor Start eljárást meghívják ezeken a példányokon.
Minden más feladat forró állapotban kezdi meg az életciklusát, ami azt jelenti, hogy az általuk képviselt aszinkron műveletek már elindultak, és a feladat állapota nem TaskStatus.Created enumerációs érték. A TAP metódusokból visszaadott összes feladatot aktiválni kell. Ha egy TAP-metódus belsőleg egy tevékenység konstruktorával hozza létre a visszaadni kívánt feladatot, a TAP metódusnak a visszaadása előtt fel kell hívnia Start az Task objektumot. A TAP metódus felhasználói nyugodtan feltételezhetik, hogy a visszaadott feladat aktív, és nem szabad megpróbálni Start meghívni a TAP metódusból visszaadott Task feladatokon. Egy aktív feladat Start meghívása InvalidOperationException kivételt eredményez.
Lemondás (nem kötelező)
A TAP-ban a lemondás nem kötelező mind az aszinkron metódus implementálói, mind az aszinkron metódusfelhasználók számára. Ha egy művelet engedélyezi a törlést, elérhetővé teszi az aszinkron metódus olyan túlterhelését, amely elfogad egy törlési tokent (CancellationToken példányt). Konvenció szerint a paraméter neve cancellationToken.
public Task ReadAsync(byte [] buffer, int offset, int count,
CancellationToken cancellationToken)
Public Function ReadAsync(buffer() As Byte, offset As Integer,
count As Integer,
cancellationToken As CancellationToken) _
As Task
Az aszinkron művelet figyeli ezt a tokent a lemondási kérelmek figyelésére. Ha lemondási kérelmet kap, dönthet úgy, hogy tiszteletben tartja a kérést, és megszakítja a műveletet. Ha a lemondási kérelem miatt a munka idő előtt fejeződik be, a TAP metódus egy Canceled állapotban végződő feladatot ad vissza; nincs elérhető eredmény, és nem történik kivételdobás. Az Canceled állapotot egy tevékenység végleges (befejezett) állapotának tekintjük, az Faulted állapotokkal együtt RanToCompletion . Ezért ha egy tevékenység a Canceled állapotban van, a IsCompleted tulajdonsága true értéket ad vissza. Amikor egy feladat a Canceled állapotban befejeződik, a feladathoz regisztrált folytatásokat ütemezik vagy végrehajtják, kivéve, ha például a NotOnCanceled lehetőséget használták a folytatás letiltására. Minden olyan kód, amely aszinkron módon vár egy törölt feladatra a programozási nyelvi funkciók használatával, továbbra is fut, de kap egy OperationCanceledException-t vagy egy abból származó kivételt. A feladatra szinkron módon várakozó kód, például WaitWaitAll egy kivétellel továbbra is futtatható.
Ha egy megszakítási token a tokent elfogadó TAP metódus meghívása előtt kérte a megszakítást, a TAP metódusnak egy Canceled feladatot kell visszaadnia. Ha azonban az aszinkron művelet futtatása közben lemondást kér, az aszinkron műveletnek nem kell elfogadnia a lemondási kérelmet. A visszaadott tevékenység csak akkor fejeződhet be az Canceled állapotban, ha a művelet a lemondási kérelem eredményeként fejeződik be. Amennyiben lemondást kérnek, de ennek ellenére egy eredmény vagy kivétel keletkezik, a feladatnak RanToCompletion vagy Faulted állapotban kell befejeződnie.
Azoknak az aszinkron metódusoknak, amelyek elsősorban a törlés lehetőségét szeretnék feltárni, nem kell olyan túlterhelést kínálniuk, ami nem fogad el törlési tokent. A nem megszakítható metódusok esetében ne adjon meg olyan túlterheléseket, amelyek elfogadják a lemondási tokent; ez segít jelezni a hívónak, hogy a célmetódus valóban megszakítható-e. Az olyan fogyasztói kód, amely nem kívánja a törlést, meghívhat egy metódust, amely elfogadja CancellationToken értéket, és argumentumértékként adja meg None. None funkcionálisan megegyezik az alapértelmezett CancellationTokenértékével.
Állapotjelentés (nem kötelező)
Az aszinkron műveletek némelyike kihasználja az előrehaladási értesítéseket; ezek általában a felhasználói felület frissítésére szolgálnak az aszinkron művelet előrehaladásával kapcsolatos információkkal.
A TAP-ban a folyamat egy IProgress<T> interfészen keresztül történik, amelyet a rendszer az aszinkron metódusnak ad át egy általában elnevezett progressparaméterként. Ha az aszinkron metódus meghívásakor megadja a folyamatkezelő felületet, azzal kiküszöbölheti a helytelen használatból eredő versenyfeltételeket (vagyis ha a művelet indítása után helytelenül regisztrált eseménykezelők esetleg kihagyják a frissítéseket). Ennél is fontosabb, hogy a folyamatjelző felület támogatja a folyamat különböző implementációit, amelyeket a fogyasztó kód határoz meg. Előfordulhat például, hogy a felhasználó kód csak a legújabb állapotfrissítéssel foglalkozik, vagy szeretné pufferelni az összes frissítést, vagy minden frissítéshez meg szeretne hívni egy műveletet, vagy szeretné szabályozni, hogy a hívás egy adott szálra van-e rendezve. Mindezek a lehetőségek az interfész egy másik, az adott fogyasztó igényeinek megfelelően testre szabott implementációjával érhetők el. A lemondáshoz hasonlóan a TAP-implementációknak is csak akkor kell paramétert IProgress<T> megadniuk, ha az API támogatja az előrehaladási értesítéseket.
Ha például a ReadAsync jelen cikkben korábban tárgyalt módszer képes az eddig beolvasott bájtok számának formájában jelenteni a köztes állapotot, a folyamat visszahívása lehet egy IProgress<T> interfész:
public Task ReadAsync(byte[] buffer, int offset, int count,
IProgress<long> progress)
Public Function ReadAsync(buffer() As Byte, offset As Integer,
count As Integer,
progress As IProgress(Of Long)) As Task
Ha egy FindFilesAsync metódus egy adott keresési mintának megfelelő összes fájl listáját adja vissza, az előrehaladási visszahívás becslést adhat a befejezett munka százalékos arányáról és a részleges eredmények aktuális készletéről. Az információt megadhatja egy tuple segítségével is:
public Task<ReadOnlyCollection<FileInfo>> FindFilesAsync(
string pattern,
IProgress<Tuple<double,
ReadOnlyCollection<List<FileInfo>>>> progress)
Public Function FindFilesAsync(pattern As String,
progress As IProgress(Of Tuple(Of Double, ReadOnlyCollection(Of List(Of FileInfo))))) _
As Task(Of ReadOnlyCollection(Of FileInfo))
vagy az API-ra jellemző adattípussal:
public Task<ReadOnlyCollection<FileInfo>> FindFilesAsync(
string pattern,
IProgress<FindFilesProgressInfo> progress)
Public Function FindFilesAsync(pattern As String,
progress As IProgress(Of FindFilesProgressInfo)) _
As Task(Of ReadOnlyCollection(Of FileInfo))
Az utóbbi esetben a speciális adattípust általában utótaggal ProgressInfoírjuk.
Ha a TAP-implementációk olyan túlterheléseket biztosítanak, amelyek elfogadják a paramétert progress , engedélyezniük kell az argumentumot null, ebben az esetben nem történik előrehaladás. A TAP-implementációknak szinkron módon kell jelentenie az előrehaladást az Progress<T> objektumnak, ami lehetővé teszi, hogy az aszinkron metódus gyorsan biztosítsa az előrehaladást. Lehetővé teszi továbbá, hogy a folyamat fogyasztója határozza meg, hogyan és hol érdemes a legjobban kezelni az információkat. A folyamatpéldány például dönthet úgy, hogy végrehajtja a visszahívásokat, és eseményeket hoz létre egy rögzített szinkronizálási környezetben.
IProgress<T-implementációk>
A .NET biztosítja az Progress<T> osztályt, amely implementálja a IProgress<T>. A Progress<T> osztály a következőképpen van deklarálva:
public class Progress<T> : IProgress<T>
{
public Progress();
public Progress(Action<T> handler);
protected virtual void OnReport(T value);
public event EventHandler<T>? ProgressChanged;
}
Az Progress<T> példány kitesz egy ProgressChanged eseményt, amely minden alkalommal kiváltódik, amikor az aszinkron művelet jelentést tesz egy állapotfrissítésről. Az ProgressChanged esemény azon az SynchronizationContext objektumon lesz előállítva, amelyet a Progress<T> példány példányosításakor rögzítettek. Ha nem volt elérhető szinkronizálási környezet, a rendszer a szálkészletet célként szolgáló alapértelmezett környezetet használja. A kezelők regisztrálhatók ezzel az eseménnyel. A konstruktor számára egyetlen kezelő is biztosítható Progress<T> a kényelem érdekében, és ugyanúgy viselkedik, mint az ProgressChanged esemény eseménykezelője. A folyamatfrissítések aszinkron módon jelennek meg, így elkerülhető az aszinkron művelet késleltetése az eseménykezelők végrehajtása közben. Egy másik IProgress<T> implementáció dönthet úgy, hogy különböző szemantikát alkalmaz.
A biztosítandó túlterhelések kiválasztása
Ha a TAP-implementáció az opcionális CancellationToken és az opcionális IProgress<T> paramétereket is használja, az akár négy túlterhelést is igényelhet:
public Task MethodNameAsync(…);
public Task MethodNameAsync(…, CancellationToken cancellationToken);
public Task MethodNameAsync(…, IProgress<T> progress);
public Task MethodNameAsync(…,
CancellationToken cancellationToken, IProgress<T> progress);
Public MethodNameAsync(…) As Task
Public MethodNameAsync(…, cancellationToken As CancellationToken cancellationToken) As Task
Public MethodNameAsync(…, progress As IProgress(Of T)) As Task
Public MethodNameAsync(…, cancellationToken As CancellationToken,
progress As IProgress(Of T)) As Task
Számos TAP-implementáció azonban nem biztosít lemondási vagy előrehaladási képességeket, ezért egyetlen módszert igényelnek:
public Task MethodNameAsync(…);
Public MethodNameAsync(…) As Task
Ha a TAP-implementáció támogatja a lemondást vagy az előrehaladást, de mindkettőt nem, két túlterhelést okozhat:
public Task MethodNameAsync(…);
public Task MethodNameAsync(…, CancellationToken cancellationToken);
// … or …
public Task MethodNameAsync(…);
public Task MethodNameAsync(…, IProgress<T> progress);
Public MethodNameAsync(…) As Task
Public MethodNameAsync(…, cancellationToken As CancellationToken) As Task
' … or …
Public MethodNameAsync(…) As Task
Public MethodNameAsync(…, progress As IProgress(Of T)) As Task
Ha a TAP-implementáció támogatja a lemondást és az előrehaladást is, az mind a négy túlterhelést elérhetővé teheti. Ez azonban csak a következő kettőt nyújthatja:
public Task MethodNameAsync(…);
public Task MethodNameAsync(…,
CancellationToken cancellationToken, IProgress<T> progress);
Public MethodNameAsync(…) As Task
Public MethodNameAsync(…, cancellationToken As CancellationToken,
progress As IProgress(Of T)) As Task
A két hiányzó köztes kombináció kompenzálása érdekében a fejlesztők megadhatják a None és a CancellationToken alapértelmezett értékét a cancellationToken paraméterhez, valamint a null értéket a progress paraméterhez.
Ha a TAP metódus minden használata támogatja a lemondást vagy az előrehaladást, kihagyhatja azokat a túlterheléseket, amelyek nem fogadják el a megfelelő paramétert.
Ha úgy dönt, hogy több túlterhelést tesz elérhetővé, hogy a lemondást vagy a haladás követését opcionálissá tegye, akkor azok a túlterhelések, amelyek nem támogatják a lemondást vagy a haladást, úgy kell viselkedjenek, mintha a None lemondást vagy a null haladás követést továbbítanák azoknak a túlterheléseknek, amelyek támogatják ezeket.
Kapcsolódó cikkek
| Cím | Leírás |
|---|---|
| Aszinkron programozási minták | Bemutatja az aszinkron műveletek végrehajtásának három mintáját: a tevékenységalapú aszinkron mintát (TAP), az aszinkron programozási modellt (APM) és az eseményalapú aszinkron mintát (EAP). |
| A feladatalapú aszinkron minta implementálása | A feladatalapú aszinkron minta (TAP) implementálását háromféleképpen ismerteti: a C# és a Visual Basic fordítóinak használatával a Visual Studióban manuálisan vagy a fordító és a manuális metódusok kombinációjával. |
| A tevékenységalapú aszinkron minta felhasználása | Hogyan érhet el blokkolás nélküli várakozást feladatok és visszahívások használatával, ismerteti. |
| Interoperabilitás más aszinkron mintákkal és típusokkal | Ismerteti, hogyan használható a feladatalapú aszinkron minta (TAP) az aszinkron programozási modell (APM) és az eseményalapú aszinkron minta (EAP) implementálásához. |