Cómo y cuándo usar 'async' y 'esperar'


1066

Según tengo entendido, una de las principales cosas que hacer asyncyawait hacer es hacer que el código sea fácil de escribir y leer, pero ¿es igual usarlos para generar hilos de fondo para realizar una lógica de larga duración?

Actualmente estoy probando el ejemplo más básico. He agregado algunos comentarios en línea. ¿Me lo puedes aclarar?

// I don't understand why this method must be marked as `async`.
private async void button1_Click(object sender, EventArgs e)
{
    Task<int> access = DoSomethingAsync();
    // task independent stuff here

    // this line is reached after the 5 seconds sleep from 
    // DoSomethingAsync() method. Shouldn't it be reached immediately? 
    int a = 1; 

    // from my understanding the waiting should be done here.
    int x = await access; 
}

async Task<int> DoSomethingAsync()
{
    // is this executed on a background thread?
    System.Threading.Thread.Sleep(5000);
    return 1;
}

48
Además, en su ejemplo, observe que recibe una advertencia cuando compila el código anterior. Presta atención a la advertencia . Te está diciendo que este código no tiene sentido.
Eric Lippert

Respuestas:


759

Cuando se usa asyncy awaitel compilador genera una máquina de estado en segundo plano.

Aquí hay un ejemplo en el que espero poder explicar algunos de los detalles de alto nivel que están sucediendo:

public async Task MyMethodAsync()
{
    Task<int> longRunningTask = LongRunningOperationAsync();
    // independent work which doesn't need the result of LongRunningOperationAsync can be done here

    //and now we call await on the task 
    int result = await longRunningTask;
    //use the result 
    Console.WriteLine(result);
}

public async Task<int> LongRunningOperationAsync() // assume we return an int from this long running operation 
{
    await Task.Delay(1000); // 1 second delay
    return 1;
}

Bien, entonces, ¿qué pasa aquí?

  1. Task<int> longRunningTask = LongRunningOperationAsync(); comienza a ejecutarse LongRunningOperation

  2. Se realiza un trabajo independiente en supongamos que await longRunningTaskse alcanza el subproceso principal (ID del subproceso = 1) .

    Ahora, si el longRunningTaskno ha terminado y todavía se está ejecutando, MyMethodAsync()volverá a su método de llamada, por lo que el hilo principal no se bloqueará. Cuando longRunningTaskfinalice, un hilo del ThreadPool (puede ser cualquier hilo) volverá a MyMethodAsync()su contexto anterior y continuará la ejecución (en este caso, imprimirá el resultado en la consola).

Un segundo caso sería que longRunningTaskya ha finalizado su ejecución y el resultado está disponible. Al llegar a la await longRunningTaskya tenemos el resultado, por lo que el código continuará ejecutándose en el mismo hilo. (en este caso, imprimir el resultado a la consola). Por supuesto, este no es el caso para el ejemplo anterior, donde hay un Task.Delay(1000)involucrado.


65
¿Por qué tenemos un "esperar" con el "Task.Delay (1000);" en el método asincrónico LongRunningOperation?
Benison Sam

3
@codea En los comentarios de Eric Lippert al artículo , vinculó un artículo introductorio a este tema en el que compara específicamente la estrategia de DoEvents con async-wait
Camilo Martinez

13
El hilo de @BenisonSam es un poco viejo, pero tenía la misma pregunta y he estado buscando una respuesta. La razón de la "espera" es que si omitimos la "espera", LongRunningOperationAsync () regresará de inmediato. De hecho, el compilador dará una advertencia si eliminamos la espera. La publicación de blog de Stephen Cleary blog.stephencleary.com/2011/09/… ofrece un excelente ejemplo de las discusiones de diseño.
shelbypereira

70
Si cada método asíncrono necesita tener una espera dentro de él, y una espera solo se puede hacer en métodos con asíncrono, ¿cuándo se detiene?
Bruno Santos

108
Esta respuesta es claramente incorrecta. Estos muchos votos positivos causarán una comprensión errónea a muchos usuarios. La documentación de MS dice claramente que no se utiliza ningún otro subproceso cuando solo se usa async, aguarde. msdn.microsoft.com/en-us/library/mt674882.aspx Por favor, alguien corrija la respuesta. Debido a esto perdí un día entero.
Krishna Deepak

171

Según tengo entendido, una de las cosas principales que asíncrono y a la espera de hacer es facilitar la escritura y lectura del código.

Deben hacer que el código asincrónico sea fácil de escribir y leer, sí.

¿Es lo mismo que generar hilos de fondo para realizar una lógica de larga duración?

De ningún modo.

// No entiendo por qué este método debe marcarse como 'asíncrono'.

La asyncpalabra clave habilita la awaitpalabra clave. Por lo tanto, cualquier método que use awaitdebe estar marcado async.

// Esta línea se alcanza después de los 5 segundos de suspensión del método DoSomethingAsync (). ¿No debería ser alcanzado de inmediato?

No, porque los asyncmétodos no se ejecutan en otro subproceso de forma predeterminada.

// ¿Se ejecuta esto en un hilo de fondo?

No.


Puede encontrar mi async/ awaitintroducción útil. Los documentos oficiales de MSDN también son inusualmente buenos (particularmente la sección TAP ), y el asyncequipo publicó una excelente pregunta frecuente .


66
Por lo tanto, no se ejecuta en un subproceso de fondo, pero tampoco se bloquea. Esto es posible debido a las API asincrónicas que usan devoluciones de llamada en lugar de hacer malabares con hilos. Inicia una operación (E / S, socket, ..) y vuelve a hacer sus cosas. Cuando se realiza la operación, el sistema operativo invocará la devolución de llamada. Esto es lo que hace Node.js o el framework Python Twisted y también tienen una buena explicación.
Roman Plášil

3
"La palabra clave asíncrona habilita la palabra clave de espera. Por lo tanto, cualquier método que use esperar debe marcarse como asíncrono", pero ¿por qué? Esta respuesta no ayuda a entender por qué el método debe marcarse como asíncrono. ¿No puede el compilador deducir que el método es asíncrono al buscar palabras clave en espera?
Stanislav

99
@Stanislav: Tengo una entrada de blog que aborda esa pregunta.
Stephen Cleary

3
Aclaración sugerida: No, porque los asyncmétodos no se ejecutan en otro hilo de manera predeterminada. En su ejemplo, la Sleep()llamada dentro DoSomethingAsync()bloquea el hilo actual que impide que la ejecución continúe dentro button1_Click()hasta que se DoSomethingAsync()complete. Tenga en cuenta que mientras Thread.Sleep()bloquea el hilo de ejecución,Task.Delay() does not.
DavidRR

166

Explicación

Aquí hay un ejemplo rápido de async/ awaita un alto nivel. Hay muchos más detalles para considerar más allá de esto.

Nota: Task.Delay(1000)simula hacer trabajo durante 1 segundo. Creo que es mejor pensar en esto como esperar una respuesta de un recurso externo. Como nuestro código está esperando una respuesta, el sistema puede dejar de lado la tarea en ejecución y volver a ella una vez que haya terminado. Mientras tanto, puede hacer otro trabajo en ese hilo.

En el siguiente ejemplo, el primer bloque está haciendo exactamente eso. Comienza todas las tareas inmediatamente (las Task.Delaylíneas) y las pone a un lado. El código se detendrá en la await alínea hasta que se haya realizado el retraso de 1 segundo antes de pasar a la siguiente línea. Dado que b, c, d, y etodo empezó ejecutar casi en el mismo momento exacto en que a(debido a la falta de la Await), deben terminar más o menos al mismo tiempo en este caso.

En el siguiente ejemplo, el segundo bloque está iniciando una tarea y esperando a que termine (eso es lo que awaithace) antes de comenzar las tareas posteriores. Cada iteración de esto lleva 1 segundo. El awaitestá pausando el programa y esperando el resultado antes de continuar. Esta es la principal diferencia entre el primer y el segundo bloque.

Ejemplo

Console.WriteLine(DateTime.Now);

// This block takes 1 second to run because all
// 5 tasks are running simultaneously
{
    var a = Task.Delay(1000);
    var b = Task.Delay(1000);
    var c = Task.Delay(1000);
    var d = Task.Delay(1000);
    var e = Task.Delay(1000);

    await a;
    await b;
    await c;
    await d;
    await e;
}

Console.WriteLine(DateTime.Now);

// This block takes 5 seconds to run because each "await"
// pauses the code until the task finishes
{
    await Task.Delay(1000);
    await Task.Delay(1000);
    await Task.Delay(1000);
    await Task.Delay(1000);
    await Task.Delay(1000);
}
Console.WriteLine(DateTime.Now);

SALIDA:

5/24/2017 2:22:50 PM
5/24/2017 2:22:51 PM (First block took 1 second)
5/24/2017 2:22:56 PM (Second block took 5 seconds)

Información adicional sobre SynchronizationContext

Nota: Aquí es donde las cosas se ponen un poco confusas para mí, así que si me equivoco en algo, corríjame y actualizaré la respuesta. Es importante tener una comprensión básica de cómo funciona esto, pero puede sobrevivir sin ser un experto en él, siempre que nunca lo use ConfigureAwait(false), aunque supongo que probablemente perderá alguna oportunidad de optimización.

Hay un aspecto de esto que hace que el concepto async/ sea awaitalgo más difícil de entender. Ese es el hecho de que en este ejemplo, todo esto está sucediendo en el mismo hilo (o al menos lo que parece ser el mismo hilo en lo que respecta a su SynchronizationContext). Por defecto, awaitrestaurará el contexto de sincronización del hilo original en el que se estaba ejecutando. Por ejemplo, en ASP.NET tiene un elemento HttpContextque está vinculado a un hilo cuando entra una solicitud. Este contexto contiene elementos específicos de la solicitud Http original, como el objeto Solicitud original que tiene elementos como idioma, dirección IP, encabezados, etc. Si cambia los hilos a la mitad del procesamiento de algo, podría terminar tratando de extraer información de este objeto en otroHttpContextque podría ser desastroso Si sabe que no usará el contexto para nada, puede optar por "no importarle". Básicamente, esto permite que su código se ejecute en un hilo separado sin traer el contexto.

¿Cómo lo logras? De manera predeterminada, el await a;código en realidad supone que DESEA capturar y restaurar el contexto:

await a; //Same as the line below
await a.ConfigureAwait(true);

Si desea permitir que el código principal continúe en un nuevo hilo sin el contexto original, simplemente use falso en lugar de verdadero para que sepa que no necesita restaurar el contexto.

await a.ConfigureAwait(false);

Una vez que el programa termina de pausarse, continuará potencialmente en un hilo completamente diferente con un contexto diferente. Aquí es de donde vendría la mejora del rendimiento: podría continuar en cualquier subproceso disponible sin tener que restaurar el contexto original con el que comenzó.

¿Es esto algo confuso? ¡Demonios si! ¿Puedes resolverlo? ¡Probablemente! Una vez que tenga una idea de los conceptos, continúe con las explicaciones de Stephen Cleary, que tienden a orientarse más hacia alguien con una comprensión técnica de async/ awaitya.


Digamos si todas estas tareas están devolviendo un int y si estoy usando el resultado de la primera tarea en la segunda tarea (o algún cálculo) ¿Sería incorrecto?
veerendra gupta

3
@veerendragupta sí. En ese caso, elegiría conscientemente no ejecutarlos de forma asincrónica (porque no son asincrónicos). También hay algunas otras cosas a tener en cuenta con respecto al contexto de configuración que no abordaré aquí
Joe Phillips

Entonces, ¿ await MethodCall()es un desperdicio absoluto? También podrías soltar el await/ async?
Vitani

2
@Jocie No del todo. Cuando llamas await, creo que libera el hilo de nuevo al grupo en lugar de retenerlo. Esto lo hace disponible para su uso en otros lugares mientras espera el regreso de la Tarea
Joe Phillips

2
@JoePhillips Creo que lo que acabas de decir es la esencia de async / wait. El hilo de llamada se libera y puede ser utilizado por otros procesos en la máquina. Cuando se completa la llamada en espera, se utiliza un nuevo hilo para reanudar lo que la persona que llamó comenzó originalmente. La persona que llama todavía está esperando, pero el beneficio es que mientras tanto se libera un hilo. ¿Ese es el beneficio de async / wait?
Bob Horn

147

Además de las otras respuestas, eche un vistazo a wait (referencia de C #)

y más específicamente en el ejemplo incluido, explica un poco su situación

El siguiente ejemplo de Windows Forms ilustra el uso de await en un método asíncrono, WaitAsynchronouslyAsync. Contraste el comportamiento de ese método con el comportamiento de WaitSynchronously. Sin un operador de espera aplicado a una tarea, WaitSynchronously se ejecuta sincrónicamente a pesar del uso del modificador asíncrono en su definición y una llamada a Thread.Sleep en su cuerpo.

private async void button1_Click(object sender, EventArgs e)
{
    // Call the method that runs asynchronously.
    string result = await WaitAsynchronouslyAsync();

    // Call the method that runs synchronously.
    //string result = await WaitSynchronously ();

    // Display the result.
    textBox1.Text += result;
}

// The following method runs asynchronously. The UI thread is not
// blocked during the delay. You can move or resize the Form1 window 
// while Task.Delay is running.
public async Task<string> WaitAsynchronouslyAsync()
{
    await Task.Delay(10000);
    return "Finished";
}

// The following method runs synchronously, despite the use of async.
// You cannot move or resize the Form1 window while Thread.Sleep
// is running because the UI thread is blocked.
public async Task<string> WaitSynchronously()
{
    // Add a using directive for System.Threading.
    Thread.Sleep(10000);
    return "Finished";
}

3
gracias por la respuesta. Pero, ¿WaitAsynchronouslyAsync () se ejecuta en un hilo separado?
Dan Dinu

32
Creo que sí, de la sección Una expresión de espera no bloquea el hilo en el que se está ejecutando. En cambio, hace que el compilador registre el resto del método asíncrono como una continuación de la tarea esperada. El control luego regresa a la persona que llama del método asíncrono. Cuando se completa la tarea, invoca su continuación y la ejecución del método asincrónico se reanuda donde se dejó.
Adriaan Stander

13
Según este artículo de MSDN , "Las palabras clave asíncronas y en espera no hacen que se creen hilos adicionales ... un método asíncrono no se ejecuta en su propio hilo". Tengo entendido que en palabras clave en espera, el marco se salta hacia adelante (de regreso a la persona que llama) para permitir que se ejecute todo el código independiente posible mientras espera que finalicen las operaciones largas. Creo que eso significa que una vez que se haya ejecutado todo el código independiente, si la operación larga no ha regresado, se bloqueará. Sin embargo, estoy aprendiendo esto ahora.
Vimes

99
@astander Eso es incorrecto. No se ejecuta en un hilo diferente. Simplemente programa la continuación (el resto del método) que se llamará cuando se Task.Delaydispare el temporizador .
MgSam

1
Esta respuesta es incorrecta debido al sueño. Vea la respuesta aceptada con await Task.Delay (1000); que tiene el comportamiento correcto
Jared Updike

62

Mostrando las explicaciones anteriores en acción en un programa de consola simple:

class Program
{
    static void Main(string[] args)
    {
        TestAsyncAwaitMethods();
        Console.WriteLine("Press any key to exit...");
        Console.ReadLine();
    }

    public async static void TestAsyncAwaitMethods()
    {
        await LongRunningMethod();
    }

    public static async Task<int> LongRunningMethod()
    {
        Console.WriteLine("Starting Long Running method...");
        await Task.Delay(5000);
        Console.WriteLine("End Long Running method...");
        return 1;
    }
}

Y la salida es:

Starting Long Running method...
Press any key to exit...
End Long Running method...

Así,

  1. Main inicia el método de larga ejecución a través de TestAsyncAwaitMethods. Eso regresa inmediatamente sin detener el hilo actual e inmediatamente vemos el mensaje 'Presione cualquier tecla para salir'
  2. Todo este tiempo, el se LongRunningMethodestá ejecutando en segundo plano. Una vez completado, otro hilo de Threadpool recoge este contexto y muestra el mensaje final

Por lo tanto, no se bloquea el hilo.


¿En qué parte de la salida se mostrará "Presione cualquier tecla para salir ..."?
StudioX

1
y para qué sirve (retorno 1)? ¿es necesario?
StudioX

1
@StudioX, creo que debe tener un tipo de retorno entero
Kuba Do

Creo que la return 1parte merece una explicación adicional: la awaitpalabra clave le permite devolver el tipo subyacente de forma Task<T>directa, lo que facilita la adaptación del código existente al mundo de espera / asíncrono . Pero no tiene que devolver un valor, ya que es posible devolver un Tasksin especificar un tipo de retorno, que sería el equivalente de un voidmétodo sincrónico . Tenga en cuenta que C # permite async voidmétodos, pero debe evitar hacerlo a menos que esté abordando controladores de eventos.
Christiano Kiss

41

Creo que has elegido un mal ejemplo con System.Threading.Thread.Sleep

El punto de una asynctarea es dejar que se ejecute en segundo plano sin bloquear el hilo principal, como hacer unDownloadFileAsync

System.Threading.Thread.Sleep no es algo que se está "haciendo", simplemente duerme y, por lo tanto, se llega a su siguiente línea después de 5 segundos ...

Lea este artículo, creo que es una gran explicación asyncy awaitconcepto: http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx


3
Por qué dormir es un mal ejemplo, pero la descarga es un buen ejemplo. Es como el tipo de cosas de FooBar cuando veo Thread.Sleep. Entiendo que hay alguna tarea que lleva tiempo. Creo que su pregunta es relevante
Abdurrahim

1
@Abdurrahim Thread.Sleepbloquea el hilo (el hilo no puede hacer nada más que permanecer inactivo), pero un método asíncrono no. En el caso de DownloadFileAsync, el hilo puede ir y hacer otra cosa hasta que llegue una respuesta del servidor remoto. Un mejor marcador de posición para "alguna tarea que lleva tiempo" en un método asíncrono es Task.Delay, ya que en realidad es asíncrono.
Gabriel Luci

@GabrielLuci mi objeción no es sobre Delay vs Sleep; Su respuesta se parece más a la respuesta de un hombre de paja; Si pones esto como un comentario a la pregunta que no sería nada, podría objetarlo, pero como respuesta huele más a una respuesta de hombre de paja. Creo que todavía está bien usar asíncrono allí, incluso todas las llamadas que tenga que hacer serán llamadas bloqueadas; No invalidará todo el propósito ... Incluso todo lo que quede será azúcar sintáctica, cuenta como un caso válido,
Abdurrahim

1
Esta no fue mi respuesta. Pero para abordar su punto: depende del propósito del método. Si solo quería un método para llamar, tuvo éxito. Pero en este caso estaba tratando de hacer un método que se ejecute de forma asincrónica. Lo hizo simplemente usando la asyncpalabra clave. Pero su método todavía se ejecutaba sincrónicamente, y esta respuesta explicaba perfectamente por qué: porque en realidad no ejecutaba ningún código asincrónico. Los métodos marcados asyncsiguen ejecutándose sincrónicamente hasta que esté awaitincompleto Task. Si no hay await, entonces el método se ejecuta sincrónicamente y el compilador le advertirá de eso.
Gabriel Luci

23

Aquí hay un programa de consola rápido para dejar en claro a los que siguen. El TaskToDométodo es su método de larga ejecución que desea hacer asíncrono. Hacer que se ejecute de forma asincrónica se realiza mediante el TestAsyncmétodo. El método de bucles de prueba solo ejecuta las TaskToDotareas y las ejecuta de forma asíncrona. Puede ver eso en los resultados porque no se completan en el mismo orden de ejecución a ejecución: informan al hilo de la interfaz de usuario de la consola cuando se completan. Simplista, pero creo que los ejemplos simplistas resaltan el núcleo del patrón mejor que los ejemplos más involucrados:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace TestingAsync
{
    class Program
    {
        static void Main(string[] args)
        {
            TestLoops();
            Console.Read();
        }

        private static async void TestLoops()
        {
            for (int i = 0; i < 100; i++)
            {
                await TestAsync(i);
            }
        }

        private static Task TestAsync(int i)
        {
            return Task.Run(() => TaskToDo(i));
        }

        private async static void TaskToDo(int i)
        {
            await Task.Delay(10);
            Console.WriteLine(i);
        }
    }
}

22

Para un aprendizaje más rápido ...

  • Comprender el flujo de ejecución del método (con un diagrama): 3 minutos

  • Pregunta introspección (aprendizaje): 1 min.

  • Pasar rápidamente la sintaxis de azúcar: 5 minutos

  • Comparta la confusión de un desarrollador: 5 minutos

  • Problema: cambie rápidamente una implementación real del código normal a código asíncrono: 2 minutos

  • ¿A dónde seguir?

Comprender el flujo de ejecución del método (con un diagrama): 3 minutos

En esta imagen, solo concéntrate en el n. ° 6 (nada más) ingrese la descripción de la imagen aquí

En el paso n. ° 6: la ejecución se detuvo aquí porque se ha quedado sin trabajo. Para continuar necesita un resultado de getStringTask (una especie de función). Por lo tanto, utiliza un awaitoperador para suspender su progreso y devolver el control (rendimiento) a la persona que llama (de este método en el que estamos). La llamada real a getStringTask se realizó anteriormente en el n. ° 2. En el n. ° 2 se hizo una promesa de devolver un resultado de cadena. ¿Pero cuándo devolverá el resultado? ¿Deberíamos (# 1: AccessTheWebAsync) hacer una segunda llamada nuevamente? Quién obtiene el resultado, # 2 (declaración de llamada) o # 6 (declaración de espera)

El llamador externo de AccessTheWebAsync () también está esperando ahora. Entonces, la persona que llama esperando AccessTheWebAsync, y AccessTheWebAsync está esperando GetStringAsync en este momento. Lo interesante es que AccessTheWebAsync trabajó un poco antes de esperar (# 4) quizás para ahorrar tiempo de espera. ¡La misma libertad para realizar múltiples tareas también está disponible para la persona que llama externa (y para todas las personas que llaman en la cadena) y esta es la mayor ventaja de esta cosa 'asincrónica'! Sientes que es sincrónico ... o normal, pero no lo es.

Recuerde, el método ya fue devuelto (# 2), no puede regresar nuevamente (sin segunda vez). Entonces, ¿cómo sabrá la persona que llama? ¡Se trata de tareas! La tarea fue aprobada. Se esperaba la tarea (sin método, sin valor). El valor se establecerá en la tarea. El estado de la tarea se configurará para completar. La persona que llama solo monitorea la Tarea (# 6). Entonces 6 # es la respuesta a dónde / quién obtiene el resultado. Más lecturas para más tarde aquí .

Pregunta introspección para aprender bien: 1 min

Ajustemos un poco la pregunta:

¿Cómo y cuándo usar y ? asyncawait Tasks

Porque el aprendizaje Taskcubre automáticamente los otros dos (y responde a su pregunta)

Pasar rápidamente la sintaxis de azúcar: 5 minutos

  • Antes de la conversión (método original)

    internal static int Method(int arg0, int arg1) { int result = arg0 + arg1; IO(); // Do some long running IO. return result; }

  • un método de tarea para llamar al método anterior

    internal static Task<int> MethodTask(int arg0, int arg1) { Task<int> task = new Task<int>(() => Method(arg0, arg1)); task.Start(); // Hot task (started task) should always be returned. return task; }

¿Mencionamos esperar o asíncrono? No. Llame al método anterior y obtendrá una tarea que puede monitorear. Ya sabes lo que devuelve la tarea ... un número entero.

  • Llamar a una tarea es un poco complicado y es cuando las palabras clave comienzan a aparecer. Llamemos MethodTask ()

    internal static async Task<int> MethodAsync(int arg0, int arg1) { int result = await HelperMethods.MethodTask(arg0, arg1); return result; }

Mismo código de arriba agregado como imagen de abajo: ingrese la descripción de la imagen aquí

  1. Estamos 'esperando' la tarea para terminar. Por lo tanto, laawait
  2. Como usamos await, debemos usar async(sintaxis obligatoria)
  3. MethodAsync con Asynccomo prefijo (estándar de codificación)

awaites fácil de entender pero los dos restantes ( async, Async) pueden no ser :). Así, se debe hacer mucho más sentido para el compilador lee though.Further para más adelante aquí

Entonces hay 2 partes.

  1. Crear 'tarea'
  2. Crear azúcar sintáctico para llamar a la tarea ( await+async)

Recuerde, teníamos una llamada externa para AccessTheWebAsync () y esa llamada tampoco se salvó ... es decir, también necesita lo mismo await+async. Y la cadena continúa. Pero siempre habrá un Tasken un extremo.

Todo está bien, pero un desarrollador se sorprendió al ver que faltaba el # 1 (Tarea) ...

Comparta la confusión de un desarrollador: 5 minutos

Un desarrollador ha cometido un error al no implementar, ¡ Taskpero aún funciona! Intente comprender la pregunta y solo la respuesta aceptada que se proporciona aquí . Espero que hayas leído y entendido completamente. El resumen es que es posible que no veamos / implementemos 'Tarea' pero se implementa en algún lugar de una clase principal. Del mismo modo, en nuestro ejemplo, llamar a un ya construido MethodAsync()es mucho más fácil que implementar ese método con un Task( MethodTask()) nosotros mismos. A la mayoría de los desarrolladores les resulta difícil entender Taskscómo convertir un código a uno asíncrono.

Consejo: intente encontrar una implementación Async existente (como MethodAsynco ToListAsync) para externalizar la dificultad. Por lo tanto, solo tenemos que lidiar con Async y esperar (que es fácil y bastante similar al código normal)

Problema: cambie rápidamente una implementación del mundo real de código normal a operación asíncrona: 2 minutos

La línea de código que se muestra a continuación en la Capa de datos comenzó a romperse (en muchos lugares). Porque actualizamos parte de nuestro código de .Net framework 4.2. * A .Net core. ¡Tuvimos que arreglar esto en 1 hora en toda la aplicación!

var myContract = query.Where(c => c.ContractID == _contractID).First();

¡pan comido!

  1. Instalamos el paquete Nuget de EntityFramework porque tiene QueryableExtensions. O en otras palabras, no la ejecución asíncrono (tarea), por lo que podría sobrevivir con una simple Asyncy awaitde código.
  2. espacio de nombres = Microsoft.EntityFrameworkCore

la línea del código de llamada se cambió así

var myContract = await query.Where(c => c.ContractID == _contractID).FirstAsync();
  1. La firma del método cambió de

    Contract GetContract(int contractnumber)

    a

    async Task<Contract> GetContractAsync(int contractnumber)

  2. el método de llamada también se vio afectado: GetContractAsync(123456);fue llamado comoGetContractAsync(123456).Result;

  3. ¡Lo cambiamos por todas partes en 30 minutos!

¡Pero el arquitecto nos dijo que no usáramos la biblioteca EntityFramework solo para esto! ¡Uy! ¡drama! Luego hicimos una implementación de Tarea personalizada (yuk). Que ya sabes cómo. Sigue siendo fácil! .. todavía yuk ..

¿A dónde seguir? Hay un maravilloso video rápido que podríamos ver sobre la conversión de llamadas síncronas a asíncronas en ASP.Net Core , quizás esa sea la dirección que tomaría después de leer esto.


respuesta fantástica! esto me ayudó mucho
cklimowski

1
Buena respuesta. Es posible que desee solucionar un par de pequeñas cosas como: (a) mención de ".Net framework 4.2" (no existe tal versión que yo sepa, existe) (b) carcasa en EntityFrameWork => EntityFramework
immitev

15

Todas las respuestas aquí usan Task.Delay()o alguna otra asyncfunción incorporada. Pero aquí está mi ejemplo que no usa ninguna de esas asyncfunciones:

// Starts counting to a large number and then immediately displays message "I'm counting...". 
// Then it waits for task to finish and displays "finished, press any key".
static void asyncTest ()
{
    Console.WriteLine("Started asyncTest()");
    Task<long> task = asyncTest_count();
    Console.WriteLine("Started counting, please wait...");
    task.Wait(); // if you comment this line you will see that message "Finished counting" will be displayed before we actually finished counting.
    //Console.WriteLine("Finished counting to " + task.Result.ToString()); // using task.Result seems to also call task.Wait().
    Console.WriteLine("Finished counting.");
    Console.WriteLine("Press any key to exit program.");
    Console.ReadLine();
}

static async Task<long> asyncTest_count()
{
    long k = 0;
    Console.WriteLine("Started asyncTest_count()");
    await Task.Run(() =>
    {
        long countTo = 100000000;
        int prevPercentDone = -1;
        for (long i = 0; i <= countTo; i++)
        {
            int percentDone = (int)(100 * (i / (double)countTo));
            if (percentDone != prevPercentDone)
            {
                prevPercentDone = percentDone;
                Console.Write(percentDone.ToString() + "% ");
            }

            k = i;
        }
    });
    Console.WriteLine("");
    Console.WriteLine("Finished asyncTest_count()");
    return k;
}

2
¡Gracias! La primera respuesta que realmente funciona en lugar de esperar.
Jeffnl

gracias por mostrar task.Wait();y cómo se puede utilizar para evitar asíncrono / demonios esperan: P
codificador

12

Esta respuesta tiene como objetivo proporcionar información específica de ASP.NET.

Al utilizar async / await en el controlador MVC, es posible aumentar la utilización del grupo de subprocesos y lograr un rendimiento mucho mejor, como se explica en el siguiente artículo,

http://www.asp.net/mvc/tutorials/mvc-4/using-asynchronous-methods-in-aspnet-mvc-4

En las aplicaciones web que ven una gran cantidad de solicitudes concurrentes al inicio o tienen una carga explosiva (donde la concurrencia aumenta repentinamente), hacer que estas llamadas de servicio web sean asíncronas aumentará la capacidad de respuesta de su aplicación. Una solicitud asíncrona tarda la misma cantidad de tiempo en procesarse que una solicitud sincrónica. Por ejemplo, si una solicitud realiza una llamada de servicio web que requiere dos segundos para completarse, la solicitud demora dos segundos si se realiza de forma síncrona o asíncrona. Sin embargo, durante una llamada asincrónica, un hilo no se bloquea para responder a otras solicitudes mientras espera a que se complete la primera solicitud. Por lo tanto, las solicitudes asíncronas evitan el colado de solicitudes y el crecimiento del grupo de subprocesos cuando hay muchas solicitudes concurrentes que invocan operaciones de larga duración.


12

Asíncrono y espera explicación simple

Analogia simple

Una persona puede esperar su tren matutino. Esto es todo lo que están haciendo, ya que esta es su tarea principal que están realizando actualmente. (programación síncrona (¡lo que normalmente haces!))

Otra persona puede esperar su tren matutino mientras fuma un cigarrillo y luego toma su café. (Programación asincrónica)

¿Qué es la programación asincrónica?

La programación asincrónica es donde un programador elegirá ejecutar parte de su código en un hilo separado del hilo principal de ejecución y luego notificará al hilo principal cuando se complete.

¿Qué hace realmente la palabra clave asíncrona?

Prefijar la palabra clave asíncrona a un nombre de método como

async void DoSomething(){ . . .

permite al programador usar la palabra clave wait al llamar a tareas asincrónicas. Eso es todo lo que hace.

¿Porque es esto importante?

En muchos sistemas de software, el hilo principal está reservado para operaciones específicamente relacionadas con la interfaz de usuario. Si estoy ejecutando un algoritmo recursivo muy complejo que tarda 5 segundos en completarse en mi computadora, pero lo estoy ejecutando en el subproceso principal (subproceso de interfaz de usuario) Cuando el usuario intenta hacer clic en cualquier cosa de mi aplicación, parecerá congelado ya que mi hilo principal se ha puesto en cola y actualmente está procesando demasiadas operaciones. Como resultado, el hilo principal no puede procesar el clic del mouse para ejecutar el método desde el clic del botón.

¿Cuándo usas Async y Await?

Utilice las palabras clave asincrónicas idealmente cuando esté haciendo algo que no implique la interfaz de usuario.

Digamos que está escribiendo un programa que permite al usuario dibujar en su teléfono móvil, pero cada 5 segundos verificará el clima en Internet.

Deberíamos estar esperando la llamada que la encuesta llama cada 5 segundos a la red para conocer el clima, ya que el usuario de la aplicación necesita seguir interactuando con la pantalla táctil del móvil para dibujar imágenes bonitas.

¿Cómo se usa Async y Await?

Siguiendo con el ejemplo anterior, aquí hay un pseudocódigo de cómo escribirlo:

    //ASYNCHRONOUS
    //this is called using the await keyword every 5 seconds from a polling timer or something.

    async Task CheckWeather()
    {
        var weather = await GetWeather();
        //do something with the weather now you have it
    }

    async Task<WeatherResult> GetWeather()
    {

        var weatherJson = await CallToNetworkAddressToGetWeather();
        return deserializeJson<weatherJson>(weatherJson);
    }

    //SYNCHRONOUS
    //This method is called whenever the screen is pressed
    void ScreenPressed()
    {
        DrawSketchOnScreen();
    }

Notas adicionales - Actualización

Olvidé mencionar en mis notas originales que en C # solo puede esperar los métodos que están envueltos en Tareas. por ejemplo, puede esperar este método:

// awaiting this will return a string.
// calling this without await (synchronously) will result in a Task<string> object.
async Task<string> FetchHelloWorld() {..

No puede esperar métodos que no sean tareas como esta:

async string FetchHelloWorld() {..

No dude en revisar el código fuente de la clase Task aquí .


44
Gracias por tomarse el tiempo para escribir este.
Prashant

10

Asíncrono / Aguardar

En realidad, Async / Await son un par de palabras clave que son simplemente azúcar sintáctica para crear una devolución de llamada de una tarea asincrónica.

Tomemos como ejemplo esta operación:

    public static void DoSomeWork()
    {
        var task = Task.Run(() =>
        {
            // [RUNS ON WORKER THREAD]

            // IS NOT bubbling up due to the different threads
            throw new Exception();
            Thread.Sleep(2000);

            return "Hello";
        });

        // This is the callback
        task.ContinueWith((t) => {
            // -> Exception is swallowed silently
            Console.WriteLine("Completed");

            // [RUNS ON WORKER THREAD]
        });
    }

El código anterior tiene varias desventajas. Los errores no se transmiten y es difícil de leer. Pero Async y Await vienen a ayudarnos:

    public async static void DoSomeWork()
    {
        var result = await Task.Run(() =>
        {
            // [RUNS ON WORKER THREAD]

            // IS bubbling up
            throw new Exception();
            Thread.Sleep(2000);

            return "Hello";
        });

        // every thing below is a callback 
        // (including the calling methods)

        Console.WriteLine("Completed");

    }

Las llamadas en espera deben estar en métodos asíncronos. Esto tiene algunas ventajas:

  • Devuelve el resultado de la tarea
  • crea automáticamente una devolución de llamada
  • comprueba si hay errores y deja que aparezcan en la pila de llamadas (solo hasta llamadas no esperadas en la pila de llamadas)
  • espera el resultado
  • libera el hilo principal
  • ejecuta la devolución de llamada en el hilo principal
  • utiliza un subproceso de trabajo del conjunto de subprocesos para la tarea
  • hace que el código sea fácil de leer
  • y mucho más

NOTA : Async y Await se usan con llamadas asincrónicas para no realizarlas. Tienes que usar Task Libary para esto, como Task.Run ().

Aquí hay una comparación entre soluciones de espera y ninguna de espera

Esta es la solución no asíncrona:

    public static long DoTask()
    {
        stopWatch.Reset();
        stopWatch.Start();

        // [RUNS ON MAIN THREAD]
        var task = Task.Run(() => {
            Thread.Sleep(2000);
            // [RUNS ON WORKER THREAD]
        });
        // goes directly further
        // WITHOUT waiting until the task is finished

        // [RUNS ON MAIN THREAD]

        stopWatch.Stop();
        // 50 milliseconds
        return stopWatch.ElapsedMilliseconds;
    }

Este es el método asíncrono:

    public async static Task<long> DoAwaitTask()
    {
        stopWatch.Reset();
        stopWatch.Start();

        // [RUNS ON MAIN THREAD]

        await Task.Run(() => {
            Thread.Sleep(2000);
            // [RUNS ON WORKER THREAD]
        });
        // Waits until task is finished

        // [RUNS ON MAIN THREAD]

        stopWatch.Stop();
        // 2050 milliseconds
        return stopWatch.ElapsedMilliseconds;
    }

En realidad, puede llamar a un método asíncrono sin la palabra clave wait pero esto significa que cualquier Excepción aquí se traga en el modo de liberación:

    public static Stopwatch stopWatch { get; } = new Stopwatch();

    static void Main(string[] args)
    {
        Console.WriteLine("DoAwaitTask: " + DoAwaitTask().Result + " ms");
        // 2050 (2000 more because of the await)
        Console.WriteLine("DoTask: " + DoTask() + " ms");
        // 50
        Console.ReadKey();
    }

Async y Await no son para computación paralela. Se utilizan para no bloquear su hilo principal. Cuando se trata de asp.net o aplicaciones de Windows, bloquear su hilo principal debido a una llamada de red es algo malo. Si haces esto, tu aplicación no responderá o incluso se bloqueará.

Echa un vistazo a ms docs para más ejemplos.


9

Para ser honesto, sigo pensando que la mejor explicación es la del futuro y las promesas en Wikipedia: http://en.wikipedia.org/wiki/Futures_and_promises

La idea básica es que tiene un grupo separado de subprocesos que ejecutan tareas de forma asincrónica. Al usarlo. Sin embargo, el objeto promete que ejecutará la operación en algún momento y le dará el resultado cuando lo solicite. Esto significa que se bloqueará cuando solicite el resultado y no haya terminado, pero de lo contrario se ejecutará en el grupo de subprocesos.

A partir de ahí, puede optimizar las cosas: algunas operaciones se pueden implementar de forma asíncrona y puede optimizar cosas como la E / S de archivos y la comunicación de red agrupando las solicitudes posteriores y / o reordenándolas. No estoy seguro de si esto ya está en el marco de tareas de Microsoft, pero si no lo fuera, sería una de las primeras cosas que agregaría.

En realidad, puede implementar el tipo de patrón futuro con rendimientos en C # 4.0. Si desea saber exactamente cómo funciona, puedo recomendar este enlace que hace un trabajo decente: http://code.google.com/p/fracture/source/browse/trunk/Squared/TaskLib/ . Sin embargo, si comienza a jugar con usted mismo, notará que realmente necesita soporte de idiomas si desea hacer todas las cosas interesantes, que es exactamente lo que hizo Microsoft.


8

Vea este violín https://dotnetfiddle.net/VhZdLU (y mejórelo si es posible) para ejecutar una aplicación de consola simple que muestra los usos de Task, Task.WaitAll (), asíncrono y espera operadores en el mismo programa.

Este violín debería borrar el concepto del ciclo de ejecución.

Aquí está el código de ejemplo

using System;
using System.Threading.Tasks;

public class Program
{
    public static void Main()
    {               
        var a = MyMethodAsync(); //Task started for Execution and immediately goes to Line 19 of the code. Cursor will come back as soon as await operator is met       
        Console.WriteLine("Cursor Moved to Next Line Without Waiting for MyMethodAsync() completion");
        Console.WriteLine("Now Waiting for Task to be Finished");       
        Task.WaitAll(a); //Now Waiting      
        Console.WriteLine("Exiting CommandLine");       
    }

    public static async Task MyMethodAsync()
    {
        Task<int> longRunningTask = LongRunningOperation();
        // independent work which doesn't need the result of LongRunningOperationAsync can be done here
        Console.WriteLine("Independent Works of now executes in MyMethodAsync()");
        //and now we call await on the task 
        int result = await longRunningTask;
        //use the result 
        Console.WriteLine("Result of LongRunningOperation() is " + result);
    }

    public static async Task<int> LongRunningOperation() // assume we return an int from this long running operation 
    {
        Console.WriteLine("LongRunningOperation() Started");
        await Task.Delay(2000); // 2 second delay
        Console.WriteLine("LongRunningOperation() Finished after 2 Seconds");
        return 1;
    }   

}

Seguimiento procedente de la ventana de salida: ingrese la descripción de la imagen aquí


3
public static void Main(string[] args)
{
    string result = DownloadContentAsync().Result;
    Console.ReadKey();
}

// You use the async keyword to mark a method for asynchronous operations.
// The "async" modifier simply starts synchronously the current thread. 
// What it does is enable the method to be split into multiple pieces.
// The boundaries of these pieces are marked with the await keyword.
public static async Task<string> DownloadContentAsync()// By convention, the method name ends with "Async
{
    using (HttpClient client = new HttpClient())
    {
        // When you use the await keyword, the compiler generates the code that checks if the asynchronous operation is finished.
        // If it is already finished, the method continues to run synchronously.
        // If not completed, the state machine will connect a continuation method that must be executed WHEN the Task is completed.


        // Http request example. 
        // (In this example I can set the milliseconds after "sleep=")
        String result = await client.GetStringAsync("http://httpstat.us/200?sleep=1000");

        Console.WriteLine(result);

        // After completing the result response, the state machine will continue to synchronously execute the other processes.


        return result;
    }
}

3

En un nivel superior:

1) La palabra clave asíncrona permite esperar y eso es todo lo que hace. La palabra clave asincrónica no ejecuta el método en un hilo separado. El método f async de inicio se ejecuta sincrónicamente hasta que llegue a esperar en una tarea que requiere mucho tiempo.

2) Puede esperar un método que devuelva Tarea o Tarea de tipo T. No puede esperar un método vacío asíncrono.

3) En el momento en que el hilo principal se encuentra en espera de una tarea que requiere mucho tiempo o cuando se inicia el trabajo real, el hilo principal vuelve al llamador del método actual.

4) Si el hilo principal ve aguardar en una tarea que aún se está ejecutando, no la espera y vuelve al llamador del método actual. De esta manera, la aplicación sigue siendo receptiva.

5) Aguardar en la tarea de procesamiento, ahora se ejecutará en un hilo separado del grupo de hilos.

6) Cuando se completa esta tarea de espera, todo el código a continuación será ejecutado por el hilo separado

A continuación se muestra el código de muestra. Ejecútelo y verifique la identificación del hilo

using System;
using System.Threading;
using System.Threading.Tasks;

namespace AsyncAwaitDemo
{
    class Program
    {
        public static async void AsynchronousOperation()
        {
            Console.WriteLine("Inside AsynchronousOperation Before AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
            //Task<int> _task = AsyncMethod();
            int count = await AsyncMethod();

            Console.WriteLine("Inside AsynchronousOperation After AsyncMethod Before Await, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            //int count = await _task;

            Console.WriteLine("Inside AsynchronousOperation After AsyncMethod After Await Before DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            DependentMethod(count);

            Console.WriteLine("Inside AsynchronousOperation After AsyncMethod After Await After DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
        }

        public static async Task<int> AsyncMethod()
        {
            Console.WriteLine("Inside AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
            int count = 0;

            await Task.Run(() =>
            {
                Console.WriteLine("Executing a long running task which takes 10 seconds to complete, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(20000);
                count = 10;
            });

            Console.WriteLine("Completed AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            return count;
        }       

        public static void DependentMethod(int count)
        {
            Console.WriteLine("Inside DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId + ". Total count is " + count);
        }

        static void Main(string[] args)
        {
            Console.WriteLine("Started Main method, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            AsynchronousOperation();

            Console.WriteLine("Completed Main method, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            Console.ReadKey();
        }

    }
}

2

La manera en que yo entiendo que es también, debe haber un tercer mandato añadido a la mezcla: Task.

Async es solo un calificador que pones en tu método para decir que es un método asincrónico.

TaskEs el retorno de la asyncfunción. Se ejecuta de forma asincrónica.

Eres awaituna tarea. Cuando la ejecución del código llega a esta línea, el control salta de nuevo a la persona que llama de su función original circundante.

Si por el contrario, se asigna el retorno de una asyncfunción (por ejemplo Task) a una variable, cuando la ejecución de código llega a esta línea, simplemente continúa más allá de esa línea en la función que rodea , mientras que las Taskejecuta de forma asíncrona.


1

¿los está utilizando para generar hilos de fondo para realizar una lógica de larga duración?

Este artículo MDSN: Programación asincrónica con asíncrono y espera (C #) lo explica explícitamente:

Las palabras clave asíncronas y en espera no hacen que se creen hilos adicionales. Los métodos asincrónicos no requieren subprocesos múltiples porque un método asincrónico no se ejecuta en su propio hilo. El método se ejecuta en el contexto de sincronización actual y usa el tiempo en el hilo solo cuando el método está activo.


1

En el siguiente código, el método HttpClient GetByteArrayAsync devuelve una tarea, getContentsTask. La tarea es una promesa de producir la matriz de bytes real cuando se completa la tarea. El operador de espera se aplica a getContentsTask para suspender la ejecución en SumPageSizesAsync hasta que se complete getContentsTask. Mientras tanto, el control se devuelve a la persona que llama de SumPageSizesAsync. Cuando getContentsTask finaliza, la expresión de espera se evalúa como una matriz de bytes.

private async Task SumPageSizesAsync()
{
    // To use the HttpClient type in desktop apps, you must include a using directive and add a 
    // reference for the System.Net.Http namespace.
    HttpClient client = new HttpClient();
    // . . .
    Task<byte[]> getContentsTask = client.GetByteArrayAsync(url);
    byte[] urlContents = await getContentsTask;

    // Equivalently, now that you see how it works, you can write the same thing in a single line.
    //byte[] urlContents = await client.GetByteArrayAsync(url);
    // . . .
}

1

A continuación se muestra el código que lee el archivo de Excel abriendo el cuadro de diálogo y luego usa asíncrono y espera para ejecutar de forma asíncrona el código que lee una a una línea de Excel y se une a la cuadrícula

namespace EmailBillingRates
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            lblProcessing.Text = "";
        }

        private async void btnReadExcel_Click(object sender, EventArgs e)
        {
            string filename = OpenFileDialog();

            Microsoft.Office.Interop.Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();
            Microsoft.Office.Interop.Excel.Workbook xlWorkbook = xlApp.Workbooks.Open(filename);
            Microsoft.Office.Interop.Excel._Worksheet xlWorksheet = xlWorkbook.Sheets[1];
            Microsoft.Office.Interop.Excel.Range xlRange = xlWorksheet.UsedRange;
            try
            {
                Task<int> longRunningTask = BindGrid(xlRange);
                int result = await longRunningTask;

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message.ToString());
            }
            finally
            {
                //cleanup  
               // GC.Collect();
                //GC.WaitForPendingFinalizers();

                //rule of thumb for releasing com objects:  
                //  never use two dots, all COM objects must be referenced and released individually  
                //  ex: [somthing].[something].[something] is bad  

                //release com objects to fully kill excel process from running in the background  
                Marshal.ReleaseComObject(xlRange);
                Marshal.ReleaseComObject(xlWorksheet);

                //close and release  
                xlWorkbook.Close();
                Marshal.ReleaseComObject(xlWorkbook);

                //quit and release  
                xlApp.Quit();
                Marshal.ReleaseComObject(xlApp);
            }

        }

        private void btnSendEmail_Click(object sender, EventArgs e)
        {

        }

        private string OpenFileDialog()
        {
            string filename = "";
            OpenFileDialog fdlg = new OpenFileDialog();
            fdlg.Title = "Excel File Dialog";
            fdlg.InitialDirectory = @"c:\";
            fdlg.Filter = "All files (*.*)|*.*|All files (*.*)|*.*";
            fdlg.FilterIndex = 2;
            fdlg.RestoreDirectory = true;
            if (fdlg.ShowDialog() == DialogResult.OK)
            {
                filename = fdlg.FileName;
            }
            return filename;
        }

        private async Task<int> BindGrid(Microsoft.Office.Interop.Excel.Range xlRange)
        {
            lblProcessing.Text = "Processing File.. Please wait";
            int rowCount = xlRange.Rows.Count;
            int colCount = xlRange.Columns.Count;

            // dt.Column = colCount;  
            dataGridView1.ColumnCount = colCount;
            dataGridView1.RowCount = rowCount;

            for (int i = 1; i <= rowCount; i++)
            {
                for (int j = 1; j <= colCount; j++)
                {
                    //write the value to the Grid  
                    if (xlRange.Cells[i, j] != null && xlRange.Cells[i, j].Value2 != null)
                    {
                         await Task.Delay(1);
                         dataGridView1.Rows[i - 1].Cells[j - 1].Value =  xlRange.Cells[i, j].Value2.ToString();
                    }

                }
            }
            lblProcessing.Text = "";
            return 0;
        }
    }

    internal class async
    {
    }
}

0

Las respuestas aquí son útiles como una guía general sobre wait / async. También contienen algunos detalles acerca de cómo se espera / async está conectado. Me gustaría compartir con ustedes alguna experiencia práctica que deben saber antes de usar este patrón de diseño.

El término "esperar" es literal, por lo que cualquier subproceso en el que lo llame esperará el resultado del método antes de continuar. En primer plano , esto es un desastre . El hilo de primer plano conlleva la carga de construir su aplicación, incluidas las vistas, los modelos de vista, las animaciones iniciales y cualquier otra cosa que haya bloqueado con esos elementos. Entonces, cuando espera el hilo de primer plano, detiene la aplicación. El usuario espera y espera cuando nada parece suceder. Esto proporciona una experiencia de usuario negativa.

Ciertamente puede esperar un hilo de fondo usando una variedad de medios:

Device.BeginInvokeOnMainThread(async () => { await AnyAwaitableMethod(); });

// Notice that we do not await the following call, 
// as that would tie it to the foreground thread.
try
{
Task.Run(async () => { await AnyAwaitableMethod(); });
}
catch
{}

El código completo para estos comentarios está en https://github.com/marcusts/xamarin-forms-annoyances . Vea la solución llamada AwaitAsyncAntipattern.sln.

El sitio de GitHub también proporciona enlaces a una discusión más detallada sobre este tema.


1
Por lo que entiendo, async / awaites el azúcar sintáctico para devoluciones de llamada, no tiene nada que ver con el enhebrado. msdn.microsoft.com/en-us/magazine/hh456401.aspx Es para el código no vinculado a la CPU, por ejemplo, esperando la entrada o un retraso. Task.Runsolo debe usarse para código enlazado a la CPU blog.stephencleary.com/2013/10/…
geometrikal

The term "await" is literal, so whatever thread you call it on will wait for the result of the method before continuing.Esto no es cierto, tal vez te refieres a Task.Wait ()? Cuando lo usa await, establece el resto del método como una continuación que se ejecutará cuando se complete lo que esperaba. Sale del método en el que lo usó, por lo que la persona que llama puede continuar. Luego, cuando la línea esperada está realmente completa, finaliza el resto de ese método en algún subproceso (generalmente un subproceso de trabajo).
Don Cheadle

@geometrikal en el núcleo, async/awaitse trata de liberar .NET Threads. Cuando realiza awaituna operación verdaderamente asíncrona (como File.WriteAsync de .NET), suspende el resto del método que utilizó await, para que la persona que llama pueda continuar y potencialmente terminar su propósito. No hay bloqueo de subprocesos o espera de la awaitoperación -ed Cuando awaitse completa la operación que editó, el resto del async/awaitmétodo se coloca en un hilo y se ejecuta (similar a una idea de devolución de llamada).
Don Cheadle
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.