run method español ejemplos create await async c# c#-5.0 async-await

method - task c#



¿Cuál es un buen ejemplo de la nueva función C#Async sin conexión a la red? (8)

Microsoft acaba de anunciar la nueva característica Cy Async . Cada ejemplo que he visto hasta ahora se trata de descargar de forma asíncrona algo de HTTP. Seguramente hay otras cosas asincrónicas importantes?

Supongamos que no estoy escribiendo un nuevo cliente RSS o una aplicación de Twitter. ¿Qué tiene de interesante C # Async para mí?

Editar ¡ Tuve un Aha! momento mientras mira la sesión de PDC de Anders . En el pasado, he trabajado en programas que usaban hilos "watcher". Estos hilos esperan a que suceda algo, como mirar un archivo para cambiar. No están trabajando, están inactivos y notifican el hilo principal cuando sucede algo. Estos hilos podrían reemplazarse con el código aguardar / asíncrono en el nuevo modelo.


Hay algunas muestras y demostraciones en el CTP que no usan Net, e incluso algunas que no hacen ninguna E / S.

Y se aplica a todas las áreas problemáticas multiproceso / paralelo (que ya existen).

Async y Await son una nueva forma (más fácil) de estructurar todo el código paralelo, ya sea vinculado a la CPU o E / S. La mayor mejora se encuentra en áreas donde antes de C # 5 tenía que usar el modelo APM (IAsyncResult) o el modelo de eventos (BackgroundWorker, WebClient). Creo que es por eso que esos ejemplos lideran el desfile ahora.


He encontrado otro buen caso de uso para esto hoy: puedes esperar la interacción del usuario.

Por ejemplo, si un formulario tiene un botón que abre otro formulario:

Form toolWindow; async void button_Click(object sender, EventArgs e) { if (toolWindow != null) { toolWindow.Focus(); } else { toolWindow = new Form(); toolWindow.Show(); await toolWindow.OnClosed(); toolWindow = null; } }

Por supuesto, esto no es más simple que

toolWindow.Closed += delegate { toolWindow = null; }

Pero creo que demuestra muy bien lo await puede hacer la await . Y una vez que el código en el controlador de eventos no sea trivial, await que la programación sea mucho más fácil. Piense en el usuario que tiene que hacer clic en una secuencia de botones:

async void ButtonSeries() { for (int i = 0; i < 10; i++) { Button b = new Button(); b.Text = i.ToString(); this.Controls.Add(b); await b.OnClick(); this.Controls.Remove(b); } }

Claro, podrías hacer esto con controladores de eventos normales, pero requeriría desmontar el bucle y convertirlo en algo mucho más difícil de entender.

Recuerde que await puede usarse con cualquier cosa que se complete en algún momento en el futuro. Aquí está el método de extensión Button.OnClick () para hacer que el trabajo anterior:

public static AwaitableEvent OnClick(this Button button) { return new AwaitableEvent(h => button.Click += h, h => button.Click -= h); } sealed class AwaitableEvent { Action<EventHandler> register, deregister; public AwaitableEvent(Action<EventHandler> register, Action<EventHandler> deregister) { this.register = register; this.deregister = deregister; } public EventAwaiter GetAwaiter() { return new EventAwaiter(this); } } sealed class EventAwaiter { AwaitableEvent e; public EventAwaiter(AwaitableEvent e) { this.e = e; } Action callback; public bool BeginAwait(Action callback) { this.callback = callback; e.register(Handler); return true; } public void Handler(object sender, EventArgs e) { callback(); } public void EndAwait() { e.deregister(Handler); } }

Desafortunadamente, no parece posible agregar el método GetAwaiter() directamente a EventHandler (lo que permite await button.Click; ) porque entonces el método no sabría cómo registrar / cancelar ese evento. Es un poco repetitivo, pero la clase AwaitableEvent se puede volver a utilizar para todos los eventos (no solo para la interfaz de usuario). Y con una pequeña modificación y añadiendo algunos genéricos, podría permitir recuperar los EventArgs:

MouseEventArgs e = await button.OnMouseDown();

Pude ver que esto es útil con algunos gestos de IU más complejos (arrastrar y soltar, gestos de ratón, ...), aunque tendrías que agregar soporte para cancelar el gesto actual.


Ooh, esto suena interesante. No estoy jugando con el CTP por el momento, solo revisando el documento técnico. Después de ver la charla de Anders Hejlsberg al respecto, creo que puedo ver cómo podría ser útil.

Según entiendo, async facilita la lectura e implementación de llamadas asíncronas. De la misma manera, escribir iteradores es ahora más fácil (en lugar de escribir la funcionalidad a mano). Este es un proceso de bloqueo esencial, ya que no se puede realizar ningún trabajo útil hasta que se desbloquee. Si estaba descargando un archivo, no puede hacer nada útil hasta que obtenga ese archivo y deje que el hilo se desperdicie. Considere cómo se podría llamar a una función que usted sabe bloqueará por una duración indeterminada y devuelve algún resultado, luego procese (por ejemplo, almacene los resultados en un archivo). ¿Cómo escribirías eso? Aquí hay un ejemplo simple:

static object DoSomeBlockingOperation(object args) { // block for 5 minutes Thread.Sleep(5 * 60 * 1000); return args; } static void ProcessTheResult(object result) { Console.WriteLine(result); } static void CalculateAndProcess(object args) { // let''s calculate! (synchronously) object result = DoSomeBlockingOperation(args); // let''s process! ProcessTheResult(result); }

Está bien, lo tenemos implementado. Pero espera, el cálculo tarda minutos en completarse. ¿Qué sucede si queremos tener una aplicación interactiva y hacer otras cosas mientras se realiza el cálculo (como la representación de la interfaz de usuario)? Esto no es bueno, ya que llamamos a la función de forma sincrónica y tenemos que esperar a que finalice la congelación de la aplicación porque el subproceso está esperando ser desbloqueado.

Responda, llame a la función costosa de manera asíncrona. De esta forma, no estamos obligados a esperar que se complete la operación de bloqueo. Pero cómo hacemos eso? Llamaríamos a la función de forma asíncrona y registraríamos una función de devolución de llamada que se llamará cuando se desbloquea para que podamos procesar el resultado.

static void CalculateAndProcessAsyncOld(object args) { // obtain a delegate to call asynchronously Func<object, object> calculate = DoSomeBlockingOperation; // define the callback when the call completes so we can process afterwards AsyncCallback cb = ar => { Func<object, object> calc = (Func<object, object>)ar.AsyncState; object result = calc.EndInvoke(ar); // let''s process! ProcessTheResult(result); }; // let''s calculate! (asynchronously) calculate.BeginInvoke(args, cb, calculate); }

  • Nota: Claro que podríamos comenzar otro hilo para hacer esto, pero eso significaría que estamos generando un hilo que está ahí esperando a ser desbloqueado, y luego haremos un trabajo útil. Eso sería un desperdicio.

Ahora la llamada es asincrónica y no tenemos que preocuparnos de esperar que el cálculo termine y procese, se hace de forma asíncrona. Terminará cuando pueda. Una alternativa al código de llamada asíncrona directamente, puede usar una Tarea:

static void CalculateAndProcessAsyncTask(object args) { // create a task Task<object> task = new Task<object>(DoSomeBlockingOperation, args); // define the callback when the call completes so we can process afterwards task.ContinueWith(t => { // let''s process! ProcessTheResult(t.Result); }); // let''s calculate! (asynchronously) task.Start(); }

Ahora llamamos a nuestra función de forma asíncrona. Pero, ¿qué se necesitó para obtenerlo de esa manera? En primer lugar, necesitábamos que el delegado / tarea pudiera llamarlo de forma asíncrona, necesitábamos una función de devolución de llamada para poder procesar los resultados y luego llamar a la función. Hemos convertido una llamada de función de dos líneas a mucho más solo para llamar a algo de forma asincrónica. No solo eso, la lógica en el código se volvió más compleja de lo que era o podría ser. Aunque utilizar una tarea ayudó a simplificar el proceso, aún necesitábamos hacer cosas para que sucediera. Solo queremos ejecutar de forma asíncrona y luego procesar el resultado. ¿Por qué no podemos hacer eso? Bueno, ahora podemos:

// need to have an asynchronous version static async Task<object> DoSomeBlockingOperationAsync(object args) { //it is my understanding that async will take this method and convert it to a task automatically return DoSomeBlockingOperation(args); } static async void CalculateAndProcessAsyncNew(object args) { // let''s calculate! (asynchronously) object result = await DoSomeBlockingOperationAsync(args); // let''s process! ProcessTheResult(result); }

Ahora bien, este era un ejemplo muy simplificado con operaciones simples (calcular, procesar). Imagínese si cada operación no se puede poner convenientemente en una función separada sino que tiene cientos de líneas de código. Esa es una gran complejidad añadida solo para obtener el beneficio de las llamadas asincrónicas.

Otro ejemplo práctico utilizado en el documento técnico es usarlo en aplicaciones de interfaz de usuario. Modificado para usar el ejemplo anterior:

private async void doCalculation_Click(object sender, RoutedEventArgs e) {     doCalculation.IsEnabled = false;     await DoSomeBlockingOperationAsync(GetArgs());     doCalculation.IsEnabled = true; }

Si ha realizado alguna programación de IU (ya sea WinForms o WPF) y ha intentado llamar a una función costosa dentro de un controlador, sabrá que es útil. Usar un trabajador de fondo para esto no sería tan útil ya que el hilo de fondo estará allí esperando hasta que pueda funcionar.

Supongamos que tiene una forma de controlar un dispositivo externo, digamos una impresora. Y quería reiniciar el dispositivo después de una falla. Naturalmente, tomará algún tiempo para que la impresora se inicie y esté lista para funcionar. Es posible que tenga que dar cuenta de que el reinicio no ayuda e intente reiniciar nuevamente. No tienes más remedio que esperarlo. No si lo hiciste de manera asincrónica.

static async void RestartPrinter() { Printer printer = GetPrinter(); do { printer.Restart(); printer = await printer.WaitUntilReadyAsync(); } while (printer.HasFailed); }

Imagina escribir el ciclo sin asincronizar.

Un último ejemplo que tengo. Imagínese si tuviera que hacer múltiples operaciones de bloqueo en una función y deseara llamar asíncronamente. ¿Qué preferirías tú?

static void DoOperationsAsyncOld() { Task op1 = new Task(DoOperation1Async); op1.ContinueWith(t1 => { Task op2 = new Task(DoOperation2Async); op2.ContinueWith(t2 => { Task op3 = new Task(DoOperation3Async); op3.ContinueWith(t3 => { DoQuickOperation(); } op3.Start(); } op2.Start(); } op1.Start(); } static async void DoOperationsAsyncNew() { await DoOperation1Async(); await DoOperation2Async(); await DoOperation3Async(); DoQuickOperation(); }

Lea el documento técnico , en realidad tiene muchos ejemplos prácticos, como escribir tareas paralelas y otros.

No puedo esperar para comenzar a jugar con esto en el CTP o cuando .NET 5.0 finalmente lo hace.


Los escenarios principales son cualquier escenario que involucre alta latencia . Es decir, mucho tiempo entre "pedir un resultado" y "obtener un resultado". Las solicitudes de red son el ejemplo más obvio de escenarios de alta latencia, seguidos de cerca por E / S en general, y luego por cómputos largos que están vinculados a la CPU en otro núcleo.

Sin embargo, hay potencialmente otros escenarios con los que esta tecnología se integrará muy bien. Por ejemplo, considere escribir la lógica de un juego de FPS. Supongamos que tiene un controlador de evento de clic de botón. Cuando el jugador hace clic en el botón, quiere tocar una sirena durante dos segundos para alertar a los enemigos y luego abrir la puerta durante diez segundos. ¿No sería lindo decir algo como:

button.Disable(); await siren.Activate(); await Delay(2000); await siren.Deactivate(); await door.Open(); await Delay(10000); await door.Close(); await Delay(1000); button.Enable();

Cada tarea se pone en cola en el subproceso de interfaz de usuario, por lo que nada bloquea, y cada uno reanuda el controlador de clic en el punto correcto una vez que finaliza su trabajo.


Un reloj GUI es un buen ejemplo; Supongamos que quiere dibujar un reloj que actualiza la hora que se muestra cada segundo. Conceptualmente, quieres escribir

while true do sleep for 1 second display the new time on the clock

y con await (o con F # async) para dormir de forma asíncrona, puede escribir este código para ejecutar en el subproceso UI de forma no bloqueante.

http://lorgonblog.wordpress.com/2010/03/27/f-async-on-the-client-side/


Este es probablemente un buen ejemplo de cómo no utilizar la nueva función de sincronización (que no está escribiendo un nuevo cliente de RSS o aplicación de Twitter), puntos de sobrecarga de método medio en una llamada de método virtual. Para ser sincero, no estoy seguro de que haya alguna forma de crear más de un punto de sobrecarga por método.

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Threading; namespace AsyncText { class Program { static void Main(string[] args) { Derived d = new Derived(); TaskEx.Run(() => d.DoStuff()).Wait(); System.Console.Read(); } public class Base { protected string SomeData { get; set; } protected async Task DeferProcessing() { await TaskEx.Run(() => Thread.Sleep(1) ); return; } public async virtual Task DoStuff() { Console.WriteLine("Begin Base"); Console.WriteLine(SomeData); await DeferProcessing(); Console.WriteLine("End Base"); Console.WriteLine(SomeData); } } public class Derived : Base { public async override Task DoStuff() { Console.WriteLine("Begin Derived"); SomeData = "Hello"; var x = base.DoStuff(); SomeData = "World"; Console.WriteLine("Mid 1 Derived"); await x; Console.WriteLine("EndDerived"); } } } }

La salida es:

Comience Derivado

Comience Base

Hola

Mid 1 Derived

Base final

Mundo

EndDerived

Con ciertas jerarquías de herencia (es decir, usando un patrón de comando) me encuentro queriendo hacer cosas como esta de vez en cuando.


Las extensiones async son útiles en algunos casos cuando tiene una operación asincrónica. Una operación asincrónica tiene un inicio y una finalización definidos . Cuando se completan las operaciones asincrónicas, pueden tener un resultado o un error. (La cancelación se trata como un tipo especial de error).

Las operaciones asincrónicas son útiles en tres situaciones (en términos generales):

  • Manteniendo su UI receptiva. Cada vez que tenga una operación de larga ejecución (ya sea que esté vinculada a la CPU o vinculada a E / S), hágalo de forma asíncrona.
  • Escalando tus servidores. Usar operaciones asincrónicas juiciosamente en el lado del servidor puede ayudar a sus servidores a escalar. por ejemplo, las páginas asincrónicas de ASP.NET pueden hacer uso de operaciones async . Sin embargo, esto no siempre es una ganancia; primero debe evaluar sus cuellos de botella de escalabilidad.
  • Proporcionar una API asincrónica limpia en una biblioteca o código compartido. async es excelente para la reutilización.

A medida que comienzas a adoptar la manera async de hacer las cosas, encontrarás que la tercera situación se está volviendo más común. async código async funciona mejor con otro código async , por lo que el tipo de código asíncrono "crece" a través de la base de código.

Hay un par de tipos de concurrencia donde async no es la mejor herramienta:

  • Paralelización. Un algoritmo paralelo puede usar muchos núcleos (CPU, GPU, computadoras) para resolver un problema más rápidamente.
  • Eventos asincrónicos Los eventos asincrónicos ocurren todo el tiempo, independientemente de su programa. A menudo no tienen una "finalización". Normalmente, su programa se suscribirá a una secuencia de eventos asincrónicos, recibirá algunas actualizaciones y luego se cancelará la suscripción . Su programa puede tratar el suscribirse y cancelar la suscripción como un "inicio" y "finalización", pero el flujo de eventos real nunca se detiene realmente.

Las operaciones paralelas se expresan mejor utilizando PLINQ o Parallel , ya que tienen una gran cantidad de compatibilidad incorporada para particionar, concurrencia limitada, etc. Una operación paralela puede envolverse fácilmente de forma segura ejecutándola desde un subproceso Task.Factory.StartNew ( Task.Factory.StartNew ).

Los eventos asincrónicos no se asignan bien a las operaciones asincrónicas. Un problema es que una operación asincrónica tiene un único resultado en su punto de finalización. Los eventos asincrónicos pueden tener cualquier cantidad de actualizaciones. Rx es el lenguaje natural para tratar con eventos asincrónicos.

Hay algunas asignaciones desde una secuencia de eventos Rx a una operación asincrónica, pero ninguna de ellas es ideal para todas las situaciones. Es más natural consumir operaciones asincrónicas con Rx, en lugar de hacerlo al revés. OMI, la mejor manera de abordar esto es utilizar las operaciones asíncronas en sus bibliotecas y el código de nivel inferior tanto como sea posible, y si necesita Rx en algún momento, entonces use Rx desde allí en adelante.


Aquí hay un artículo sobre cómo mostrar cómo usar la sintaxis ''async'' en un escenario no conectado que involucra UI y múltiples acciones.