¿Alguien puede explicar qué hace la await
función?
c#
c#-5.0
async-await
Chris Nicol
fuente
fuente
Respuestas:
Ellos sólo hablamos de esto en el PDC ayer!
Await se usa junto con Tasks (programación paralela) en .NET. Es una palabra clave que se introduce en la próxima versión de .NET. Más o menos le permite "pausar" la ejecución de un método para esperar a que la tarea complete la ejecución. He aquí un breve ejemplo:
//create and run a new task Task<DataTable> dataTask = new Task<DataTable>(SomeCrazyDatabaseOperation); //run some other code immediately after this task is started and running ShowLoaderControl(); StartStoryboard(); //this will actually "pause" the code execution until the task completes. It doesn't lock the thread, but rather waits for the result, similar to an async callback // please so also note, that the task needs to be started before it can be awaited. Otherwise it will never return dataTask.Start(); DataTable table = await dataTask; //Now we can perform operations on the Task result, as if we're executing code after the async operation completed listBoxControl.DataContext = table; StopStoryboard(); HideLoaderControl();
fuente
Básicamente, las palabras clave
async
y leawait
permiten especificar que la ejecución de un método debe detenerse en todos los usos deawait
, que marcan las llamadas a métodos asincrónicos, y luego reanudar una vez que se completa la operación asincrónica. Esto le permite llamar a un método en el hilo principal de una aplicación y manejar el trabajo complejo de forma asincrónica, sin la necesidad de definir explícitamente hilos y uniones o bloquear el hilo principal de la aplicación.Piense en ello como algo similar a una
yield return
declaración en un método que produce un IEnumerable. Cuando el tiempo de ejecuciónyield
llegue al, básicamente guardará el estado actual del método y devolverá el valor o la referencia que se obtiene. La próxima vez que se llame a IEnumerator.MoveNext () en el objeto de retorno (que es generado internamente por el tiempo de ejecución), el estado anterior del método se restaura en la pila y la ejecución continúa con la siguiente línea después delyield return
como si nunca hubiéramos dejado el método. Sin esta palabra clave, un tipo IEnumerator debe definirse de forma personalizada para almacenar el estado y manejar las solicitudes de iteración, con métodos que pueden volverse MUY complejos.Del mismo modo, un método marcado como
async
debe tener al menos unoawait
. En unawait
tiempo, el tiempo de ejecución guardará el estado del hilo actual y la pila de llamadas, realizará la llamada asincrónica y volverá al ciclo de mensajes del tiempo de ejecución para manejar el siguiente mensaje y mantener la aplicación receptiva. Cuando se completa la operación asíncrona, en la siguiente oportunidad de programación, la pila de llamadas para aumentar la operación asíncrona se devuelve y continúa como si la llamada fuera síncrona.Por lo tanto, estas dos nuevas palabras clave básicamente simplifican la codificación de procesos asincrónicos, al igual que
yield return
simplificaron la generación de enumerables personalizados. Con un par de palabras clave y un poco de conocimiento previo, puede omitir todos los detalles confusos y, a menudo, propensos a errores de un patrón asincrónico tradicional. Esto será INVALUABLE en prácticamente cualquier aplicación GUI impulsada por eventos como Winforms, WPF de Silverlight.fuente
La respuesta actualmente aceptada es engañosa.
await
no está pausando nada. En primer lugar, solo se puede usar en métodos o lambdas marcados comoasync
y regresandoTask
ovoid
si no le importa que laTask
instancia se ejecute en este método.Aquí hay una ilustración:
internal class Program { private static void Main(string[] args) { var task = DoWork(); Console.WriteLine("Task status: " + task.Status); Console.WriteLine("Waiting for ENTER"); Console.ReadLine(); } private static async Task DoWork() { Console.WriteLine("Entered DoWork(). Sleeping 3"); // imitating time consuming code // in a real-world app this should be inside task, // so method returns fast Thread.Sleep(3000); await Task.Run(() => { for (int i = 0; i < 10; i++) { Console.WriteLine("async task iteration " + i); // imitating time consuming code Thread.Sleep(1000); } }); Console.WriteLine("Exiting DoWork()"); } }
Salida:
fuente
await
? ¿Significa que si esto se llama desde un hilo de la interfaz de usuario, bloqueará el hilo de la interfaz de usuario durante 3 segundos? La idea de este modelo es evitar hacer cosas así.Para cualquier persona nueva en la programación asincrónica en .NET, aquí hay una analogía (totalmente falsa) en un escenario con el que puede estar más familiarizado: llamadas AJAX usando JavaScript / jQuery. Una publicación simple de jQuery AJAX se ve así:
$.post(url, values, function(data) { // AJAX call completed, do something with returned data here });
La razón por la que procesamos los resultados en una función de devolución de llamada es para que no bloqueemos el hilo actual mientras esperamos que regrese la llamada AJAX. Solo cuando la respuesta esté lista, se activará la devolución de llamada, liberando el hilo actual para hacer otras cosas mientras tanto.
Ahora, si JavaScript admite la
await
palabra clave (que por supuesto no lo hace ( ¡todavía! )), Podría lograr lo mismo con esto:var data = await $.post(url, values); // AJAX call completed, do something with returned data here
Eso es mucho más limpio, pero seguro que parece que introdujimos un código de bloqueo sincrónico. Pero el compilador de JavaScript (falso) habría tomado todo después
await
y lo habría conectado a una devolución de llamada, por lo que en tiempo de ejecución el segundo ejemplo se comportaría como el primero.Puede que no parezca que le está ahorrando mucho trabajo, pero cuando se trata de cosas como el manejo de excepciones y los contextos de sincronización, el compilador en realidad está haciendo un gran trabajo pesado por usted. Para obtener más información, recomendaría las preguntas frecuentes seguidas de la serie de blogs de Stephen Cleary .
fuente
Si tuviera que implementarlo en Java, se vería así:
/** * @author Ilya Gazman */ public abstract class SynchronizedTask{ private ArrayList<Runnable> listeners = new ArrayList<Runnable>(); private static final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(6, 6, 0, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(1000)); public final void await(Runnable listener){ synchronized (this) { listeners.add(listener); } } public void excecute(){ onExcecute(); for (int i = listeners.size() - 1; i >= 0; i--) { Runnable runnable; synchronized (this) { runnable = listeners.remove(i); } threadPoolExecutor.execute(runnable); } } protected abstract void onExcecute(); }
Su aplicación lo usaría así:
public class Test{ private Job job = new Job(); public Test() { craeteSomeJobToRunInBackground(); methode1(); methode2(); } private void methode1(){ System.out.println("Running methode 1"); job.await(new Runnable() { @Override public void run() { System.out.println("Continue to running methode 1"); } }); } private void methode2(){ System.out.println("Running methode 2"); } private void craeteSomeJobToRunInBackground() { new Thread(new Runnable() { @Override public void run() { job.excecute(); } }).start(); } private class Job extends SynchronizedTask{ @Override protected void onExcecute() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Job is done"); } } }
fuente