Funzioni asincrone e attività

FSharp.Core 11 aggiunge funzioni per l'interoperabilità delle attività, le trasformazioni dei risultati e la concorrenza associata. Usarli con i flussi di lavoro asincroni esistenti e le espressioni di attività; non richiedono il prototipo di runtime-async.

Attendere le operazioni di .NET da asincrona

Per avviare un'operazione di .NET con il token di annullamento del flusso di lavoro asincrono, passare una factory a Async.StartTaskImmediate:

open System.IO

let tryReadFile path =
    async {
        try
            let! contents =
                Async.StartTaskImmediate(fun ct -> File.ReadAllTextAsync(path, ct))
            return Some contents
        with :? FileNotFoundException ->
            return None
    }

Il file letto riceve il token di annullamento del flusso di lavoro. Un file mancante restituisce None. Una lettura completata restituisce Some contents. Il flusso di lavoro attende il completamento, incluso l'annullamento e la pulizia.

Usare Async.Await quando si ha già un valore simile a un'attività. Entrambe le API propagano direttamente l'unica eccezione sottostante di un'attività con errore. Un'aggregazione con più eccezioni interne rimane un'aggregazione. Async.AwaitTask mantiene il comportamento di wrapping delle eccezioni esistente.

API Forme di argomenti/risultati supportate
Async.Await Task<'T> -> Async<'T>; Task -> Async<unit>
Async.Await ValueTask<'T> -> Async<'T>; ValueTask -> Async<unit>
Async.Await Overload inline simile all'attività usando il GetAwaiter protocollo .
Async.StartTaskImmediate (CancellationToken -> Task<'T>) -> Async<'T>; non generico Task restituisce Async<unit>.
Async.StartTaskImmediate Overload di factory corrispondenti ValueTask, ValueTask<'T>, e simili alle attività.
Task.startAsyncImmediate CancellationToken -> Async<'T> -> Task<'T>
Async.RunSynchronouslyImmediate Async<'T> * ?cancellationToken: CancellationToken -> 'T

Async.RunSynchronouslyImmediate inizia sul thread chiamante, a differenza Async.RunSynchronously di un thread in primo piano e blocca per il completamento. Non ha alcun parametro di timeout. Preferisce la composizione asincrona per l'interfaccia utente e i thread di richiesta.

Esporre un flusso di lavoro asincrono come attività

Per offrire il flusso di lavoro di lettura dei file precedente ai chiamanti basati su attività, accettare un token di annullamento e passarlo tramite Task.startAsyncImmediate:

let tryReadFileTask (path: string) (ct: CancellationToken) =
    tryReadFile path |> Task.startAsyncImmediate ct

Il bridge restituisce Task<string option> e propaga l'annullamento. Inizia sul thread chiamante fino alla prima sospensione; le continuazioni successive non devono usare tale thread.

Concorrenza associata

Un Async<'T> valore descrive il lavoro posticipato. Un'attività rappresenta in genere un'operazione già avviata. Per limitare l'uso delle risorse, descrivere il lavoro delle attività come factory anziché le attività già in esecuzione:

open System.IO
open System.Threading

let readFiles (paths: seq<string>) (ct: CancellationToken) =
    paths
    |> Seq.map (fun path ->
        fun token -> File.ReadAllTextAsync(path, token))
    |> Task.parallelLimit 4 ct

Task.parallelLimit inizia al massimo il limite positivo di calcoli contemporaneamente e restituisce i risultati in ordine di input. L'ordine di avvio e l'ordine di completamento sono altrimenti non specificati. Per la concorrenza maggiore di uno, ogni factory riceve un token collegato che segnala anche un errore di pari livello; per l'esecuzione sequenziale riceve direttamente il token fornito.

Quando si verifica un errore di calcolo, viene richiesto l'annullamento per i relativi elementi di pari livello. Le operazioni devono osservare i token per cooperare. Se diversi calcoli hanno esito negativo, viene propagata un'eccezione; non dipendere da quale errore vince.

Function Forma della firma
Task.parallelLimit int -> CancellationToken -> seq<CancellationToken -> Task<'T>> -> Task<'T[]>
Task.parallelDoLimit int -> CancellationToken -> seq<CancellationToken -> Task<unit>> -> Task<unit>
Task.sequential CancellationToken -> seq<CancellationToken -> Task<'T>> -> Task<'T[]>
Task.sequentialDo CancellationToken -> seq<CancellationToken -> Task<unit>> -> Task<unit>
Async.parallelLimit int -> seq<Async<'T>> -> Async<'T[]>
Async.parallelDoLimit int -> seq<Async<unit>> -> Async<unit>
Async.sequentialDo seq<Async<unit>> -> Async<unit>

Le Async funzioni propagano il token di annullamento dell'ambiente del flusso di lavoro. Esistente Async.Parallel (inclusa l'opzione relativa al limite di concorrenza) e Async.Sequential rimane disponibile. Le Do varianti espongono un unit risultato quando è necessario solo il completamento. Un limite di concorrenza non è un limite di velocità: le operazioni veloci possono comunque produrre molte richieste al secondo.

Trasformare i risultati e recuperare da errori

I Microsoft.FSharp.Control.Asyncmoduli , Taske ValueTask espongono queste funzioni qualificate. M<'T> nella colonna shape è l'acronimo del tipo di calcolo corrispondente, non di un'abbreviazione del tipo F#.

Funzioni qualificate Shape
Async.result, Task.result, ValueTask.result 'T -> M<'T>
Async.map, Task.map, ValueTask.map ('T -> 'U) -> M<'T> -> M<'U>
Async.bind, Task.bind, ValueTask.bind ('T -> M<'U>) -> M<'T> -> M<'U>
Async.ignore<'T>, Task.ignore<'T>, ValueTask.ignore<'T> M<'T> -> M<unit>; argomento di tipo di input esplicito obbligatorio.
Async.catchWith, Task.catchWith, ValueTask.catchWith (exn -> 'T) -> M<'T> -> M<'T>
Async.catch, Task.catch, ValueTask.catch M<'T> -> M<Result<'T, exn>>
Async.empty, Task.empty, ValueTask.empty M<unit>
Task.ofValueTask ValueTask<'T> -> Task<'T>
ValueTask.ofTask Task<'T> -> ValueTask<'T>

Ad esempio, eseguire il mapping del contenuto di un file a una lunghezza, quindi rappresentare un errore di lettura come Result:

open System.IO
open System.Threading

let fileLength (path: string) (ct: CancellationToken) =
    File.ReadAllTextAsync(path, ct)
    |> Task.map String.length
    |> Task.catch

Un file contenente Report 1 restituisce Ok 8. Un file mancante fornisce un oggetto Error contenente un oggetto FileNotFoundException. Per eliminare un valore riuscito anziché trasformarlo, specificare il tipo di input quando si chiama ignore:

let completion: System.Threading.Tasks.Task<unit> =
    Task.result 42 |> Task.ignore<int>

catchWith fornisce un valore sostitutivo per un errore non di annullamento; non esegue un altro calcolo asincrono. catch restituisce Ok o Error. L'annullamento non viene convertito in un valore di errore o passato al gestore di ripristino. Task.empty è un Task<unit>oggetto , non l'oggetto non generico Task.CompletedTask.

A differenza di , Async.catchesistente restituisce Async.CatchChoice. Il ripristino gestisce gli errori del calcolo fornito, non le eccezioni sincrone generate prima della chiamata. Async rimane posticipato; le attività rimangono ansiose.

Disponibilità di ValueTask e libreria

Le API ValueTask richiedono la netstandard2.1 compilazione o net10.0 FSharp.Core 11. Vedere Selezione della destinazione principale.

Osservare il contratto a consumo singolo ValueTask: usare ogni valore una sola volta, non tramite await o pipeline separate. Per le attese ripetute, chiamare Task.ofValueTask una sola volta e riutilizzare solo l'attività risultante.

Vedere anche