Está en la página 1de 5

Programación asíncrona

Hoy en día la velocidad de los procesos, y por ende de los programa es crucial. Disponemos de procesadores
muy potentes y como buenos diseñadores y programadores tenemos que saber sacar partido al alto rendimiento
y procesamiento que nos permiten las máquinas.

Un procesador no puede quedar esperándose sin trabajo que realizar. Es decir, no podemos ser bloqueantes.
Tenemos que saber ordenar las operaciones que realizamos, lanzar antes aquellas que preveemos que las
utilizaremos más tarde y consumirán tiempo de procesador.

Entender como funciona la asincronía se convierte en algo fundamental si queremos hacer aplicaciones rápidas
y eficientes. El problema es que la asincronía introduce algo de complejidad a nuestro código y debemos ser
cuidadosos a la hora de utilizarla, poniendo especial atención en cumplir las buenas prácticas que nos evitarán
dolores de cabeza.

Diferencia entre Task y Thread

Un thread representa un hilo real a nivel de sistema operativo, con sus propios recursos de pila. Permite el
grado máximo de control en la programación asíncrona. Se pueden anular, suspender o reanudar hilos. También
se puede saber su estado e incluso cambiar propiedades como el tamaño de la pila.

El problema de los threads es que son costosos. Cada hilo consume una zona importante de memoria para
albergar la pila de hilos y también consume recursos de procesador al tener que gestionar políticas de cambios,
priorizarión y en general reglas y comportamiento de los threads.

El uso de threads es recomendable cuando no hay alternativa. Por ejemplo para especificar un nombre al thread
para poder debugar.

ThreadPool es un contenedor de Threads. Es un grupo de subprocesos mantenidos por el CLR. Con threadpool
no se tiene el grado de control que se puede tener con un thread. Se pueden enviar procesos a ejecutar al pool de
threads en cualquier momento e incluso tener un control del tamaño del contenedor, pero poco más se puede
hacer.

No se puede indicar cuando se empezarán a ejecutarse los threads dentrol del pool, ni tan siquiera se sabe
cuando un determinado hilo acabó. El uso adecuado sería por tanto para operaciones cortas donde el que llama
al hilo no necesita el resultado.

Por útimo la clase Task de la  Task Parallel Library ofrece lo mejor de ambos. Un task no crea su propio hilo de
OS pero si que mantiene un planificador de hilos, el TaskScheduler. Task, permite también saber cuando el hilo
termina y retorna un resultado a través de Task<T>.Los objetos Task permiten esperar un resultado, al contrario
que los ThreadPool.

En general podríamos decir que salvo contadas excepciones, la mejor opción es usar Task.
Await 
Para indicar que debemos esperar un resultado que está corriendo en otro hilo distinto al nuestro de ejecución se
utiliza la instrucción await.

Un buen ejemplo que podemos encontrar aquí, y nos sirve para entender como funcionan estos métodos, es el
siguiente:

En el punto 2, getStringTask se divide en otro hilo y el hilo de procesamiento de nuestro programa sigue
ejecutando la siguiente instrucción, que en este caso es DoIndepentWork(). Al acabar esta función, debemos
esperar a getStringTask, si no había acabado nos quedaremos aquí detenidos, hasta que tengamos el valor del
resultado final. Si en esta instrucción no hubiera un await, el resultado sería impredecible.

Es importante tener en cuenta que el hecho de que un método sea cualificado con ASYNC no indica que se vaya
a ejecutar asíncronamente, necesitaremos la instrucción await para crear otro thread.

Que se puede hacer con un objeto Task


Podemos cancelar un objeto Task. Imaginemos que damos un tiempo para realizar una tarea y en caso que esta
no vuelva en el tiempo que hemos determinado, queremos que dicha tarea se cancele. Para esto utilizaremos el
método cancel del objecto CancellationTokenSource.

WhenAll permiter esperar un conjunto de tareas y WhenAny devolverá la primera tarea disponible de un


conjunto de tareas.

Run. Encola un conjunto de  de tareas en el ThreadPool. En general, un task.Run no es un paradigma de


programación asíncrona.

Contenedores asíncronos 
ConcurrentDictionary<TKey,TValue> es la versión asíncrona de Dictionary<TKey, TValue>. Las diferencias
que tiene ambos son las siguientes:

 El método Add(), que implementaba  Dictionary se sustituye por TryAdd(), AddOrUpdate(), or


GetOrAdd().
 Remove() se sustituye por TryRemove y para updatear valores se utiliza TryUpdate().
 Las propiedades Count, Keys, Values y el método GetEnumerator() se debe utilizar con precaución,
debido a la naturaleza asíncrona de las operaciones que vamos a realizar.

Lo mismo sucede con las clases Stack Class  y Queue Class  clásicas de .NET . Su versión asíncrona
son ConcurrentStack y ConcurrentQueue.

 Los respectivos métodos de Pop() and Push() se reemplezan por TryPop()/TryPopRange para arrays y
TryPush().
 El método Peek(), que devuelve el primer objeto de la pila sin eliminarlo, se sustituye por TryPeek().
Ventajas, desventajas y dónde usar el asincronismo
Las ventajas principales que aporta el uso de la programación asíncrona son:

 La aplicación podrá soportar más usuarios. En las aplicaciones web cada sesión de usuario crea un
thread. Cuando se hacen peticiones con await, el thread de ejecución se libera, pudiendo ser utilizado
por otra tarea. El uso de async en las apliaciones web, está bien en este video
[https://channel9.msdn.com/Events/TechDays/Techdays-2012-the-Netherlands/2287]
 Se pueden procesar distintos procesos de Entrada/Salida en paralelo
 Se puede hacer una interfaz  más agradable al usuario. Se puede procesas cierta acción en segundo plano
y devolver el control al usuario hasta que esta acción acaba.

Se recomienda usar async en DBs, WebServices, external API’s, Web Services, Compute-Bound tasks.
Por último el inconvinente de async es sobre todo, que hace mucho más dificil enteder un código y poder
debugarlo. Async aporta velocidad pero genera complejidad a nuestro código y consumo de memoria y CPU.

Buenas prácticas 
Stephen Cleary, el gurú de la asincronía en .NET y autor del libro Corruncy in c#, tiene una serie de artículos
que vale la pena leer acerca del uso conveniente de la programación asíncrona.El primero de ellos, forma parte
de la documentación de Microsoft. Entre ellas, algunas recomendaciones importantes son:

 Evitar métodos asíncronos que devuelvan void. Si hay algún error no sabremos de donde viene. Una
buena aproximación es crear un Task watcher, como hacer el propio Stephen Cleary en este artículo , en
la sección A Better Approach.
 Mezclar código síncrono y asíncrono teniendo especial cuidado.
 Llamar a métodos asíncronos de forma síncrona, esto se hace con Task.Run(). A no ser que sea
necesario, también se considera una mala práctica.
 Configurar el contexto await con ConfigureAwait siempre que sea posible. Aumentarás el paralelismo y
podrás evitar deadlocks.
 Pero no usar ConfigureAwait cuando se tiene código inmediatamente después del await que necesita del
contexto.

Vistas parciales
Uno de los problemas de la asincronía en MVC se presenta cuando usamos ActionResult, concretamente
cuando se devuelve un tipo PartialViewResult.

Las vistas parciales no pueden ser asíncronas, es decir, siempre serán síncronas. Esto quedará resuelto en la
nueva versión de MVC6, se sustituyen los Html.Action() por View Components, los cuales podremos hacer
asíncronos en la propia vista. Pero por el momento es una clara limitación en un entorno MVC.

De todos modos, si no estás en la nueva versión de MVC 6, existe una solución y es utilizar jquery para
renderizar las vistas parciales como asíncronas. Aquí tienes un buen artículo de como hacerlo.

Algunos enlaces consultados para el artículo:


https://www.simple-talk.com/dotnet/.net-framework/the-.net-4.5-asyncawait-feature-in-promise-and-practice/
http://blog.stephencleary.com/2013/11/taskrun-etiquette-examples-dont-use.html
http://elvex.ugr.es/decsai/csharp/pdf/parallel/0-concurrency.pdf
http://www.tutorialspoint.com/csharp/csharp_stack.htm
https://msdn.microsoft.com/en-us/magazine/dn605875.aspx

También podría gustarte