Programación asincrónica en F#Async programming in F#

La programación asincrónica es un mecanismo fundamental para las aplicaciones modernas por diversos motivos.Asynchronous programming is a mechanism that is essential to modern applications for diverse reasons. Hay dos casos de uso principales en los que se encontrarán la mayoría de los desarrolladores:There are two primary use cases that most developers will encounter:

  • Presentar un proceso de servidor que pueda dar servicio a un número significativo de solicitudes entrantes simultáneas, al tiempo que se minimizan los recursos del sistema ocupados mientras el procesamiento de solicitudes espera entradas de sistemas o servicios externos a ese proceso.Presenting a server process that can service a significant number of concurrent incoming requests, while minimizing the system resources occupied while request processing awaits inputs from systems or services external to that process
  • Mantener una interfaz de usuario con capacidad de respuesta o un subproceso principal mientras progresa simultáneamente el trabajo en segundo planoMaintaining a responsive UI or main thread while concurrently progressing background work

Aunque el trabajo en segundo plano implica la utilización de varios subprocesos, es importante tener en cuenta los conceptos de asincronía y multithreading por separado.Although background work often does involve the utilization of multiple threads, it's important to consider the concepts of asynchrony and multi-threading separately. De hecho, son aspectos independientes y uno no implica el otro.In fact, they are separate concerns, and one does not imply the other. En este artículo se describen los conceptos independientes con más detalle.This article describes the separate concepts in more detail.

Asincronía definidoAsynchrony defined

El punto anterior, que asincronía es independiente del uso de varios subprocesos, merece la pena explicar un poco más.The previous point - that asynchrony is independent of the utilization of multiple threads - is worth explaining a bit further. Hay tres conceptos que a veces están relacionados, pero estrictamente independientes entre sí:There are three concepts that are sometimes related, but strictly independent of one another:

  • Simultaneidad Cuando se ejecutan varios cálculos en períodos de tiempo superpuestos.Concurrency; when multiple computations execute in overlapping time periods.
  • Paralelismo cuando varios cálculos o varias partes de un único cálculo se ejecutan exactamente al mismo tiempo.Parallelism; when multiple computations or several parts of a single computation run at exactly the same time.
  • Asincronía Cuando uno o varios cálculos pueden ejecutarse por separado desde el flujo del programa principal.Asynchrony; when one or more computations can execute separately from the main program flow.

Los tres son conceptos ortogonales, pero se pueden reagrupar fácilmente, especialmente cuando se usan juntos.All three are orthogonal concepts, but can be easily conflated, especially when they are used together. Por ejemplo, puede que necesite ejecutar varios cálculos asincrónicos en paralelo.For example, you may need to execute multiple asynchronous computations in parallel. Esta relación no significa que el paralelismo o asincronía impliquen entre sí.This relationship does not mean that parallelism or asynchrony imply one another.

Si tiene en cuenta el etymology de la palabra "Asynchronous", hay dos partes implicadas:If you consider the etymology of the word "asynchronous", there are two pieces involved:

  • "a", que significa "no"."a", meaning "not".
  • "sincrónico", que significa "al mismo tiempo"."synchronous", meaning "at the same time".

Al colocar estos dos términos juntos, verá que "asincrónico" significa "no al mismo tiempo".When you put these two terms together, you'll see that "asynchronous" means "not at the same time". Eso es todo.That's it! No hay ninguna implicación de simultaneidad o paralelismo en esta definición.There is no implication of concurrency or parallelism in this definition. Esto también se aplica en la práctica.This is also true in practice.

En términos prácticos, los cálculos asincrónicos en F # están programados para ejecutarse independientemente del flujo principal del programa.In practical terms, asynchronous computations in F# are scheduled to execute independently of the main program flow. Esta ejecución independiente no implica simultaneidad ni paralelismo, ni implica que un cálculo siempre se produce en segundo plano.This independent execution doesn't imply concurrency or parallelism, nor does it imply that a computation always happens in the background. De hecho, los cálculos asincrónicos pueden incluso ejecutarse sincrónicamente, dependiendo de la naturaleza del cálculo y del entorno en el que se ejecuta el cálculo.In fact, asynchronous computations can even execute synchronously, depending on the nature of the computation and the environment the computation is executing in.

La principal ventaja que debe tener es que los cálculos asincrónicos son independientes del flujo principal del programa.The main takeaway you should have is that asynchronous computations are independent of the main program flow. Aunque hay pocas garantías sobre cuándo o cómo se ejecuta un cálculo asincrónico, existen algunos enfoques para orquestarlos y programarlos.Although there are few guarantees about when or how an asynchronous computation executes, there are some approaches to orchestrating and scheduling them. En el resto de este artículo se exploran los conceptos básicos de F # asincronía y cómo usar los tipos, las funciones y las expresiones integradas en F #.The rest of this article explores core concepts for F# asynchrony and how to use the types, functions, and expressions built into F#.

Conceptos principalesCore concepts

En F #, la programación asincrónica se centra en torno a tres conceptos básicos:In F#, asynchronous programming is centered around three core concepts:

  • El Async<'T> tipo, que representa un cálculo asincrónico que admite composición.The Async<'T> type, which represents a composable asynchronous computation.
  • Las Async funciones del módulo, que permiten programar el trabajo asincrónico, componer los cálculos asincrónicos y transformar los resultados asincrónicos.The Async module functions, which let you schedule asynchronous work, compose asynchronous computations, and transform asynchronous results.
  • async { } Expresión de cálculo, que proporciona una sintaxis adecuada para compilar y controlar los cálculos asincrónicos.The async { } computation expression, which provides a convenient syntax for building and controlling asynchronous computations.

Puede ver estos tres conceptos en el ejemplo siguiente:You can see these three concepts in the following example:

open System
open System.IO

let printTotalFileBytes path =
    async {
        let! bytes = File.ReadAllBytesAsync(path) |> Async.AwaitTask
        let fileName = Path.GetFileName(path)
        printfn $"File {fileName} has %d{bytes.Length} bytes"
    }

[<EntryPoint>]
let main argv =
    printTotalFileBytes "path-to-file.txt"
    |> Async.RunSynchronously

    Console.Read() |> ignore
    0

En el ejemplo, la printTotalFileBytes función es de tipo string -> Async<unit> .In the example, the printTotalFileBytes function is of type string -> Async<unit>. La llamada a la función no ejecuta realmente el cálculo asincrónico.Calling the function does not actually execute the asynchronous computation. En su lugar, devuelve un Async<unit> que actúa como especificación del trabajo que se va a ejecutar de forma asincrónica.Instead, it returns an Async<unit> that acts as a specification of the work that is to execute asynchronously. Llama Async.AwaitTask a en su cuerpo, que convierte el resultado de ReadAllBytesAsync en un tipo adecuado.It calls Async.AwaitTask in its body, which converts the result of ReadAllBytesAsync to an appropriate type.

Otra línea importante es la llamada a Async.RunSynchronously .Another important line is the call to Async.RunSynchronously. Esta es una de las funciones de inicio del módulo Async que debe llamar si desea ejecutar realmente un cálculo asincrónico de F #.This is one of the Async module starting functions that you'll need to call if you want to actually execute an F# asynchronous computation.

Esta es una diferencia fundamental con el estilo C#/Visual Basic de async programación.This is a fundamental difference with the C#/Visual Basic style of async programming. En F #, los cálculos asincrónicos se pueden considerar como tareas en frío.In F#, asynchronous computations can be thought of as Cold tasks. Deben iniciarse explícitamente para ejecutarse realmente.They must be explicitly started to actually execute. Esto tiene algunas ventajas, ya que permite combinar y secuenciar el trabajo asincrónico de forma mucho más sencilla que en C# o Visual Basic.This has some advantages, as it allows you to combine and sequence asynchronous work much more easily than in C# or Visual Basic.

Combinar cálculos asincrónicosCombine asynchronous computations

Este es un ejemplo que se basa en el anterior mediante la combinación de cálculos:Here is an example that builds upon the previous one by combining computations:

open System
open System.IO

let printTotalFileBytes path =
    async {
        let! bytes = File.ReadAllBytesAsync(path) |> Async.AwaitTask
        let fileName = Path.GetFileName(path)
        printfn $"File {fileName} has %d{bytes.Length} bytes"
    }

[<EntryPoint>]
let main argv =
    argv
    |> Seq.map printTotalFileBytes
    |> Async.Parallel
    |> Async.Ignore
    |> Async.RunSynchronously

    0

Como puede ver, la main función tiene bastantes más elementos.As you can see, the main function has quite a few more elements. Conceptualmente, hace lo siguiente:Conceptually, it does the following:

  1. Transforme los argumentos de la línea de comandos en una secuencia de Async<unit> cálculos con Seq.map .Transform the command-line arguments into a sequence of Async<unit> computations with Seq.map.
  2. Cree un Async<'T[]> que programe y ejecute los printTotalFileBytes cálculos en paralelo cuando se ejecute.Create an Async<'T[]> that schedules and runs the printTotalFileBytes computations in parallel when it runs.
  3. Cree un Async<unit> que ejecutará el cálculo en paralelo y omitirá su resultado (que es unit[] ).Create an Async<unit> that will run the parallel computation and ignore its result (which is a unit[]).
  4. Ejecute explícitamente el cálculo compuesto general con Async.RunSynchronously , y bloquee hasta que se complete.Explicitly run the overall composed computation with Async.RunSynchronously, blocking until it completes.

Cuando se ejecuta este programa, printTotalFileBytes se ejecuta en paralelo para cada argumento de la línea de comandos.When this program runs, printTotalFileBytes runs in parallel for each command-line argument. Dado que los cálculos asincrónicos se ejecutan de forma independiente del flujo de programa, no hay ningún orden definido en el que impriman su información y terminen de ejecutarse.Because asynchronous computations execute independently of program flow, there is no defined order in which they print their information and finish executing. Los cálculos se programarán en paralelo, pero no se garantiza su orden de ejecución.The computations will be scheduled in parallel, but their order of execution is not guaranteed.

Cálculos asincrónicos de secuenciaSequence asynchronous computations

Dado Async<'T> que es una especificación de trabajo en lugar de una tarea que ya se está ejecutando, puede realizar fácilmente transformaciones más complejas.Because Async<'T> is a specification of work rather than an already-running task, you can perform more intricate transformations easily. Este es un ejemplo que secuencia un conjunto de cálculos asincrónicos para que se ejecuten uno tras otro.Here is an example that sequences a set of Async computations so they execute one after another.

let printTotalFileBytes path =
    async {
        let! bytes = File.ReadAllBytesAsync(path) |> Async.AwaitTask
        let fileName = Path.GetFileName(path)
        printfn $"File {fileName} has %d{bytes.Length} bytes"
    }

[<EntryPoint>]
let main argv =
    argv
    |> Seq.map printTotalFileBytes
    |> Async.Sequential
    |> Async.Ignore
    |> Async.RunSynchronously
    |> ignore

Esto programará printTotalFileBytes para que se ejecute en el orden de los elementos de en argv lugar de programarlos en paralelo.This will schedule printTotalFileBytes to execute in the order of the elements of argv rather than scheduling them in parallel. Dado que cada operación sucesiva no se programará hasta que el cálculo anterior haya terminado de ejecutarse, los cálculos se ordenan de modo que no se superponen en su ejecución.Because each successive operation will not be scheduled until after the preceding computation has finished executing, the computations are sequenced such that there is no overlap in their execution.

Funciones importantes del módulo AsyncImportant Async module functions

Al escribir código asincrónico en F #, normalmente interactuará con un marco que controla la programación de los cálculos.When you write async code in F#, you'll usually interact with a framework that handles scheduling of computations for you. Sin embargo, este no es siempre el caso, por lo que es conveniente comprender las distintas funciones que se pueden usar para programar el trabajo asincrónico.However, this is not always the case, so it is good to understand the various functions that can be used to schedule asynchronous work.

Dado que los cálculos asincrónicos de F # son una especificación del trabajo en lugar de una representación del trabajo que ya se está ejecutando, deben iniciarse explícitamente con una función de inicio.Because F# asynchronous computations are a specification of work rather than a representation of work that is already executing, they must be explicitly started with a starting function. Hay muchos métodos de inicio asincrónico que son útiles en contextos diferentes.There are many Async starting methods that are helpful in different contexts. En la siguiente sección se describen algunas de las funciones de inicio más comunes.The following section describes some of the more common starting functions.

Async. Startchild (Async.StartChild

Inicia un cálculo secundario dentro de un cálculo asincrónico.Starts a child computation within an asynchronous computation. Esto permite ejecutar simultáneamente varios cálculos asincrónicos.This allows multiple asynchronous computations to be executed concurrently. El cálculo secundario comparte un token de cancelación con el cálculo primario.The child computation shares a cancellation token with the parent computation. Si se cancela el cálculo primario, también se cancela el cálculo de los elementos secundarios.If the parent computation is canceled, the child computation is also canceled.

Signature:Signature:

computation: Async<'T> * timeout: ?int -> Async<Async<'T>>

Cuándo usarlo:When to use:

  • Cuando se desea ejecutar varios cálculos asincrónicos simultáneamente en lugar de uno en uno, pero no se programan en paralelo.When you want to execute multiple asynchronous computations concurrently rather than one at a time, but not have them scheduled in parallel.
  • Cuando desea asociar la duración de un cálculo secundario al de un cálculo primario.When you wish to tie the lifetime of a child computation to that of a parent computation.

Qué debe ver:What to watch out for:

  • Iniciar varios cálculos con Async.StartChild no es lo mismo que programarlos en paralelo.Starting multiple computations with Async.StartChild isn't the same as scheduling them in parallel. Si desea programar cálculos en paralelo, use Async.Parallel .If you wish to schedule computations in parallel, use Async.Parallel.
  • Si se cancela un cálculo primario, se desencadenará la cancelación de todos los cálculos secundarios que se iniciaron.Canceling a parent computation will trigger cancellation of all child computations it started.

Async. StartImmediate (Async.StartImmediate

Ejecuta un cálculo asincrónico y comienza inmediatamente en el subproceso actual del sistema operativo.Runs an asynchronous computation, starting immediately on the current operating system thread. Esto resulta útil si necesita actualizar algo en el subproceso de llamada durante el cálculo.This is helpful if you need to update something on the calling thread during the computation. Por ejemplo, si un cálculo asincrónico debe actualizar una interfaz de usuario (por ejemplo, actualizar una barra de progreso), Async.StartImmediate debe usarse.For example, if an asynchronous computation must update a UI (such as updating a progress bar), then Async.StartImmediate should be used.

Signature:Signature:

computation: Async<unit> * cancellationToken: ?CancellationToken -> unit

Cuándo usarlo:When to use:

  • Cuando es necesario actualizar algo en el subproceso que realiza la llamada en medio de un cálculo asincrónico.When you need to update something on the calling thread in the middle of an asynchronous computation.

Qué debe ver:What to watch out for:

  • El código del cálculo asincrónico se ejecutará en cualquier subproceso en el que se programe.Code in the asynchronous computation will run on whatever thread one happens to be scheduled on. Esto puede ser problemático si ese subproceso es sensiblemente confidencial, como un subproceso de la interfaz de usuario.This can be problematic if that thread is in some way sensitive, such as a UI thread. En tales casos, Async.StartImmediate es probable que no sea apropiado usar.In such cases, Async.StartImmediate is likely inappropriate to use.

Async. Startastask (Async.StartAsTask

Ejecuta un cálculo en el grupo de subprocesos.Executes a computation in the thread pool. Devuelve un Task<TResult> que se completará en el estado correspondiente una vez finalizado el cálculo (genera el resultado, produce la excepción o se cancela).Returns a Task<TResult> that will be completed on the corresponding state once the computation terminates (produces the result, throws exception, or gets canceled). Si no se proporciona ningún token de cancelación, se usará el token de cancelación predeterminado.If no cancellation token is provided, then the default cancellation token is used.

Signature:Signature:

computation: Async<'T> * taskCreationOptions: ?TaskCreationOptions * cancellationToken: ?CancellationToken -> Task<'T>

Cuándo usarlo:When to use:

  • Cuando necesita llamar a una API de .NET que produce un Task<TResult> para representar el resultado de un cálculo asincrónico.When you need to call into a .NET API that yields a Task<TResult> to represent the result of an asynchronous computation.

Qué debe ver:What to watch out for:

  • Esta llamada asignará un Task objeto adicional, lo que puede aumentar la sobrecarga si se usa con frecuencia.This call will allocate an additional Task object, which can increase overhead if it is used often.

Async. ParallelAsync.Parallel

Programa una secuencia de cálculos asincrónicos que se van a ejecutar en paralelo, produciendo una matriz de resultados en el orden en el que se proporcionaron.Schedules a sequence of asynchronous computations to be executed in parallel, yielding an array of results in the order they were supplied. El grado de paralelismo se puede optimizar o limitar opcionalmente especificando el maxDegreeOfParallelism parámetro.The degree of parallelism can be optionally tuned/throttled by specifying the maxDegreeOfParallelism parameter.

Signature:Signature:

computations: seq<Async<'T>> * ?maxDegreeOfParallelism: int -> Async<'T[]>

Cuándo usarloWhen to use it:

  • Si necesita ejecutar un conjunto de cálculos al mismo tiempo y no depende de su orden de ejecución.If you need to run a set of computations at the same time and have no reliance on their order of execution.
  • Si no necesita resultados de los cálculos programados en paralelo hasta que todos se hayan completado.If you don't require results from computations scheduled in parallel until they have all completed.

Qué debe ver:What to watch out for:

  • Solo se puede tener acceso a la matriz de valores resultante una vez finalizados todos los cálculos.You can only access the resulting array of values once all computations have finished.
  • Los cálculos se ejecutarán cuando acaben de programarse.Computations will be run whenever they end up getting scheduled. Este comportamiento significa que no se puede confiar en su orden de ejecución.This behavior means you cannot rely on their order of their execution.

Async. SequentialAsync.Sequential

Programa una secuencia de cálculos asincrónicos que se van a ejecutar en el orden en que se pasan.Schedules a sequence of asynchronous computations to be executed in the order that they are passed. Se ejecutará el primer cálculo, después el siguiente, y así sucesivamente.The first computation will be executed, then the next, and so on. No se ejecutarán cálculos en paralelo.No computations will be executed in parallel.

Signature:Signature:

computations: seq<Async<'T>> -> Async<'T[]>

Cuándo usarloWhen to use it:

  • Si tiene que ejecutar varios cálculos en orden.If you need to execute multiple computations in order.

Qué debe ver:What to watch out for:

  • Solo se puede tener acceso a la matriz de valores resultante una vez finalizados todos los cálculos.You can only access the resulting array of values once all computations have finished.
  • Los cálculos se ejecutarán en el orden en que se pasan a esta función, lo que puede significar que habrá más tiempo antes de que se devuelvan los resultados.Computations will be run in the order that they are passed to this function, which can mean that more time will elapse before the results are returned.

Async. Awaittask (Async.AwaitTask

Devuelve un cálculo asincrónico que espera Task<TResult> a que se complete el determinado y devuelve su resultado como un Async<'T>Returns an asynchronous computation that waits for the given Task<TResult> to complete and returns its result as an Async<'T>

Signature:Signature:

task: Task<'T> -> Async<'T>

Cuándo usarlo:When to use:

  • Cuando se utiliza una API de .NET que devuelve un Task<TResult> en un cálculo asincrónico de F #.When you are consuming a .NET API that returns a Task<TResult> within an F# asynchronous computation.

Qué debe ver:What to watch out for:

  • Las excepciones se incluyen en AggregateException la siguiente Convención de la biblioteca Parallel de tarea; este comportamiento es diferente de la forma en que F # Async normalmente muestra excepciones.Exceptions are wrapped in AggregateException following the convention of the Task Parallel Library; this behavior is different from how F# async generally surfaces exceptions.

Async. CatchAsync.Catch

Crea un cálculo asincrónico que ejecuta un determinado Async<'T> y devuelve un Async<Choice<'T, exn>> .Creates an asynchronous computation that executes a given Async<'T>, returning an Async<Choice<'T, exn>>. Si el especificado Async<'T> se completa correctamente, Choice1Of2 se devuelve un con el valor resultante.If the given Async<'T> completes successfully, then a Choice1Of2 is returned with the resultant value. Si se produce una excepción antes de que se complete, Choice2of2 se devuelve un con la excepción generada.If an exception is thrown before it completes, then a Choice2of2 is returned with the raised exception. Si se usa en un cálculo asincrónico que se compone de muchos cálculos y uno de esos cálculos produce una excepción, el cálculo de la englobación se detendrá por completo en su totalidad.If it is used on an asynchronous computation that is itself composed of many computations, and one of those computations throws an exception, the encompassing computation will be stopped entirely.

Signature:Signature:

computation: Async<'T> -> Async<Choice<'T, exn>>

Cuándo usarlo:When to use:

  • Cuando se realiza el trabajo asincrónico que puede producir un error con una excepción y se desea controlar esa excepción en el llamador.When you are performing asynchronous work that may fail with an exception and you want to handle that exception in the caller.

Qué debe ver:What to watch out for:

  • Cuando se usan cálculos asincrónicos combinados o secuenciados, el cálculo de la englobación se detendrá por completo si uno de sus cálculos "internos" produce una excepción.When using combined or sequenced asynchronous computations, the encompassing computation will fully stop if one of its "internal" computations throws an exception.

Async. ignoreAsync.Ignore

Crea un cálculo asincrónico que ejecuta el cálculo dado pero quita su resultado.Creates an asynchronous computation that runs the given computation but drops its result.

Signature:Signature:

computation: Async<'T> -> Async<unit>

Cuándo usarlo:When to use:

  • Cuando tiene un cálculo asincrónico cuyo resultado no es necesario.When you have an asynchronous computation whose result is not needed. Esto es análogo a la ignore función para el código no asincrónico.This is analogous to the ignore function for non-asynchronous code.

Qué debe ver:What to watch out for:

  • Si debe usar Async.Ignore porque desea utilizar Async.Start u otra función que requiera Async<unit> , considere la posibilidad de descartar el resultado.If you must use Async.Ignore because you wish to use Async.Start or another function that requires Async<unit>, consider if discarding the result is okay. Evite descartar los resultados solo para ajustarse a una firma de tipo.Avoid discarding results just to fit a type signature.

Async. RunSynchronouslyAsync.RunSynchronously

Ejecuta un cálculo asincrónico y espera su resultado en el subproceso que realiza la llamada.Runs an asynchronous computation and awaits its result on the calling thread. Propaga una excepción en caso de que el cálculo produzca una.Propagates an exception should the computation yield one. Esta llamada está bloqueando.This call is blocking.

Signature:Signature:

computation: Async<'T> * timeout: ?int * cancellationToken: ?CancellationToken -> 'T

Cuándo usarloWhen to use it:

  • Si lo necesita, úselo solo una vez en una aplicación: en el punto de entrada de un archivo ejecutable.If you need it, use it only once in an application - at the entry point for an executable.
  • Cuando no le interesa el rendimiento y desea ejecutar un conjunto de otras operaciones asincrónicas a la vez.When you don't care about performance and want to execute a set of other asynchronous operations at once.

Qué debe ver:What to watch out for:

  • La llamada a Async.RunSynchronously bloquea el subproceso que realiza la llamada hasta que se completa la ejecución.Calling Async.RunSynchronously blocks the calling thread until the execution completes.

Async. StartAsync.Start

Inicia un cálculo asincrónico que devuelve unit en el grupo de subprocesos.Starts an asynchronous computation that returns unit in the thread pool. No espera a que se completen o observan un resultado de excepción.Doesn't wait for its completion and/or observe an exception outcome. Los cálculos anidados iniciados con Async.Start se inician independientemente del cálculo primario que los llamó; su duración no está asociada a ningún cálculo primario.Nested computations started with Async.Start are started independently of the parent computation that called them; their lifetime is not tied to any parent computation. Si se cancela el cálculo primario, no se cancelan los cálculos secundarios.If the parent computation is canceled, no child computations are canceled.

Signature:Signature:

computation: Async<unit> * cancellationToken: ?CancellationToken -> unit

Use solo cuando:Use only when:

  • Tiene un cálculo asincrónico que no produce un resultado ni requiere procesamiento de uno.You have an asynchronous computation that doesn't yield a result and/or require processing of one.
  • No es necesario saber cuándo se completa un cálculo asincrónico.You don't need to know when an asynchronous computation completes.
  • No le importa en qué subproceso se ejecuta un cálculo asincrónico.You don't care which thread an asynchronous computation runs on.
  • No es necesario tener en cuenta ni notificar las excepciones resultantes de la ejecución.You don't have any need to be aware of or report exceptions resulting from the execution.

Qué debe ver:What to watch out for:

  • Las excepciones producidas por los cálculos iniciados con Async.Start no se propagan al autor de la llamada.Exceptions raised by computations started with Async.Start aren't propagated to the caller. La pila de llamadas se desenredará por completo.The call stack will be completely unwound.
  • Cualquier trabajo (como llamar a printfn ) iniciado con Async.Start no hará que el efecto se produzca en el subproceso principal de la ejecución de un programa.Any work (such as calling printfn) started with Async.Start won't cause the effect to happen on the main thread of a program's execution.

Interoperar con .NETInteroperate with .NET

Puede estar trabajando con una biblioteca de .NET o código base de C# que utiliza la programación asincrónica de estilo Async/Await.You may be working with a .NET library or C# codebase that uses async/await-style asynchronous programming. Dado que C# y la mayoría de las bibliotecas de .NET usan los Task<TResult> Task tipos y como abstracciones principales en lugar de Async<'T> , debe cruzar un límite entre estos dos enfoques a asincronía.Because C# and the majority of .NET libraries use the Task<TResult> and Task types as their core abstractions rather than Async<'T>, you must cross a boundary between these two approaches to asynchrony.

Cómo trabajar con .NET Async y Task<T>How to work with .NET async and Task<T>

Trabajar con las bibliotecas asincrónicas .NET y códigos base que usan Task<TResult> (es decir, los cálculos asincrónicos que tienen valores devueltos) es sencillo y tiene compatibilidad integrada con F #.Working with .NET async libraries and codebases that use Task<TResult> (that is, async computations that have return values) is straightforward and has built-in support with F#.

Puede usar la Async.AwaitTask función para esperar un cálculo asincrónico de .net:You can use the Async.AwaitTask function to await a .NET asynchronous computation:

let getValueFromLibrary param =
    async {
        let! value = DotNetLibrary.GetValueAsync param |> Async.AwaitTask
        return value
    }

Puede usar la Async.StartAsTask función para pasar un cálculo asincrónico a un llamador de .net:You can use the Async.StartAsTask function to pass an asynchronous computation to a .NET caller:

let computationForCaller param =
    async {
        let! result = getAsyncResult param
        return result
    } |> Async.StartAsTask

Cómo trabajar con .NET Async y TaskHow to work with .NET async and Task

Para trabajar con las API que usan Task (es decir, los cálculos asincrónicos de .net que no devuelven un valor), puede que necesite agregar una función adicional que convierta un Async<'T> en un Task :To work with APIs that use Task (that is, .NET async computations that do not return a value), you may need to add an additional function that will convert an Async<'T> to a Task:

module Async =
    // Async<unit> -> Task
    let startTaskFromAsyncUnit (comp: Async<unit>) =
        Async.StartAsTask comp :> Task

Ya existe una Async.AwaitTask que acepta Task como entrada.There is already an Async.AwaitTask that accepts a Task as input. Con esta y la función definida anteriormente startTaskFromAsyncUnit , puede iniciar y esperar Task tipos de un cálculo asincrónico de F #.With this and the previously defined startTaskFromAsyncUnit function, you can start and await Task types from an F# async computation.

Relación con Multi-ThreadingRelationship to multi-threading

Aunque los subprocesos se mencionan en este artículo, hay dos aspectos importantes que hay que recordar:Although threading is mentioned throughout this article, there are two important things to remember:

  1. No hay ninguna afinidad entre un cálculo asincrónico y un subproceso, a menos que se inicie explícitamente en el subproceso actual.There is no affinity between an asynchronous computation and a thread, unless explicitly started on the current thread.
  2. La programación asincrónica en F # no es una abstracción para multithreading.Asynchronous programming in F# is not an abstraction for multi-threading.

Por ejemplo, un cálculo puede ejecutarse realmente en el subproceso del llamador, dependiendo de la naturaleza del trabajo.For example, a computation may actually run on its caller's thread, depending on the nature of the work. Un cálculo también podría "saltar" entre subprocesos, por lo que se le prestará una pequeña cantidad de tiempo para realizar un trabajo útil entre los períodos de "en espera" (por ejemplo, cuando una llamada de red está en tránsito).A computation could also "jump" between threads, borrowing them for a small amount of time to do useful work in between periods of "waiting" (such as when a network call is in transit).

Aunque F # proporciona algunas funciones para iniciar un cálculo asincrónico en el subproceso actual (o explícitamente no en el subproceso actual), asincronía generalmente no está asociado a una estrategia de subprocesos determinada.Although F# provides some abilities to start an asynchronous computation on the current thread (or explicitly not on the current thread), asynchrony generally is not associated with a particular threading strategy.

Consulta tambiénSee also