Concurrency in c# – Part 2

Bien continuando con concurrencia vamos a ejemplificar lo dicho en la parte 1.
Continuemos con Async/Await – Asynchronous Programming

Vamos hacer 3 ejemplos básicos. Todo el código aquí generado estará subido en mi github: https://github.com/morales-franco/SpotyApp

Aclaró que el nombre se debe a que finalmente consumiremos la Api de Spotify explotando async-await pero eso será más adelante, promise :p task :p

Bien para el ejemplo vamos a usar una app mvc y un controller llamado Radio con el que intentaremos ejemplificar lo que vimos antes
Básicamente vamos a tener 3 métodos que harán lo mismo. Tendremos un ActionMethod que nos devuelve un model que tiene 3 propiedades:
1) Una lista de albums
2) Una lista de artistas
3) Cantidad de usuarios que estan escuchando la radio en este momento.

Para obtener estos datos tenemos un RadioService que simula la llamada a un WebService, demorando un tiempo en particular:
1) Obtener lista de albums –> tarda 2000 ms
2) Obtener lista de artistas –> tarda 2000 ms
3) Cant. de oyentes –> tarda 1000 ms

Let’s start!

Ejemplo 1 – Sin concurrencia, la vida Sync #secuencial


Resultado:

Bien aqui vemos como al ser sincronico demora un delay total de 5000 ms.
5000ms = 1000ms( get Cant. de oyentes ) + 2000ms(get albums) + 2000ms (get artists)

Ejemplo 2 – Transformándolo en Async


Bien ahora vamos hacerlo async a los metodos y evaluaremos el comportamiento:

Bien evaluamos el comportamiento antes de ejecutarlo.
Agregamos async a la firma del método y necesitamos que nuestro método tenga al menos un await y obviamente devolveremos una Task.
Entonces tenemos métodos nuevos en nuestro service que ahora son async:

Bien volviendo al controller la ejecución es la siguiente:

  1. Un método async comienza a ejecutarse de manera Sincrónica hasta que encuentra el primer await.
  2. En la linea 49 se encuentra el primer await entonces en ese momento pasa lo siguiente:
    1. El método de suspende, retornamos una task incomplete liberando el thread para que por ejemplo pueda atender otro request.
    2. En background vamos a buscar los Album’s al servicio
    3. Cuando tengamos los album’s (2000ms) la tarea notifica que termino. Entonces toma el contexto y contina ejecutando.
  3. Linea 50 sucede lo mismo que explicamos antes, se suspende el proceso y se van a buscar los artistas liberando el thread. Luego cuando se obtengan los artistas se reanuda la ejecución. (2000ms)
  4. Linea 51 sucede lo mismo explicado anteriormente. (1000ms)
  5. Finalmente se devuelve la vista

Bien en este caso notamos que si bien usamos async y explotamos los recursos del servidor, liberando threads en tiempo de respuesta NO tenemos mejoras. Estamos ejecutando código async que se comporta sincronicamente.
Verifiquemos esto:

Ejemplo 3- Explotamos concurrencia, la vida Async

Bien ahora vamos a sacar ventaja de async en tiempo respuesta además de aprovechamiento de recursos.
Analicemos el siguiente caso:

  1. El método se ejecuta en forma sincrónica hasta que encuentra el primer await. Entonces en la linea 69 vamos al service y le decimos “consulta al web service y traeme los albums” entonces básicamente mandamos a buscar los abum’s en background y como no hay un await continuamos
  2. En la linea 70, nuevamente le decimos “che service, anda buscarme los artista al WebService”, no hay await entonces continuamos
  3. En la linea 71 sucede lo mismo vamos al service y le decimos que contacte al Web Service y continuamos la ejecución.
  4. Entonces en la linea 72 tenemos que ser consientes que tenemos 3 Tasks que se están ejecutando en paralelo, esto es potencia pura! Mandamos a ejecutar tareas y continuamos procesando.
  5. En la linea 74 esta el primer await entonces hay el compilador dice ok albumsPromise ya tenes el resultado? ya finalizaste? Si es sí entonces continua ejecutando la linea 75 sino suspende el método y libera el thread.
  6. Una vez que termina albumsPromise se avanza a la linea 75. Hay nuevamente el se verifica si la task artistPromise termino y de acuerdo a eso se suspende el metodo o se continua la ejecución a la linea 76
  7. Una vez que finaliza la task artistPromise continuamos con la linea 76. Nuevamente nos encontramos con un await entonces verificamos que la task numberOfListenersPromise haya finalizado o no para ver si suspendemos el metodo o continuamos.
  8. Una vez finalizada la promise numberOfListenersPromise continuamos y finalmente devolvemos la vista.

Bien en teoría al tener tareas paralelas y async deberíamos tener mejor tiempo de respuesta que los ejemplos 1 y2. Veremos!

Magic!

Evidentemente mejoramos la performace terriblemente! de 5 segundos a 2! más de un 50% de performance ganada!

Bien espero haber sido claro, y nos vemos en la parte 3 donde hablaremos un poco más en detalle acerca de las Tasks!

Saludos
FM!