Parallel Extensions es un framework de Microsoft, aún en desarrollo para facilitar el desarrollo de aplicaciones concurrentes. Recientemente acaban de sacar la primera CTP (comunity tecnology preview) que está disponible para descarga y han solicitado feedback y comentarios en sus foros.
El funcionamiento es el común de una biblioteca, pero la implementación incorpora optimizaciones para mejorar el rendimiento de las aplicaciones multihilo liberando al programador de la tarea. Internamente se encarga de "decidir" si un trabajo debe ser ejecutado en un hilo separado o si se debe ejecutar en el propio hilo que invoca la llamada. Internamente está organizado en una serie de hilos, parecido al thread pool de .NET pero con ciertas optimizaciones, ya que existen una serie de colas internas en las que se insertan los trabajos (es algo más complicado que eso pero baste la simplificación), permitiendo que determinados hilos se "roben" las tareas entre ellos para mejorar la ocupación general de la CPU y por tanto el rendimiento del programa.
Hace bien poco he leido un interesante artículo (en inglés) llamado "The empty try block mystery" que describe algunas secciones de código presentes en el código fuente del .NET Framework y que son similares a lo siguiente:
y que responde a la pregunta ¿porque diablos iba alguien a declarar un bloque try finally estando el try vacío? Pues vamos a explicarlo, en español ;)
Acabo de leer un artículo bastante interesante sobre el rendimiento del uso de excepciones en .NET (aunque en general el concepto es aplicable a prácticamente cualquier lenguaje que las soporte).
El manejado de una excepción, especialmente la parte correspondiente a su resolución implica un gran consumo de recursos y tiempo. Para los que no vayan muy bien con el inglés (o sean demasiado vagos para leer el artículo :P) lo que viene a demostrar es la diferencia entre estos dos segmentos de código
|
for (int i = 0; i < 1000000; i++)
{ string s = null; try{ string ss = s.Substring(0, 2); } catch (System.NullReferenceException) { } } |
for (int i = 0; i < 1000000; i++)
{ string s = null; if (s != null) { string ss = s.Substring(0, 2); } } |
observando que el código de la derecha tarda medio segundo frente a los nada más y nada menos que 50 segundos del código de la izquierada.
No obstante, aunque la comparación así hecha parezca nefasta, no debemos olvidar que la prueba lanza un total de un millon de excepciones consecutivas, algo que no es muy probable que pasé en la vida real, no obstante, si podemos comprobar que una llamada sea correcta antes de realizar, siempre será mucho mejor que capturar la excepción. En ese sentido es importante recalcar que lo importante es evitar la excepción en la medida de lo posible, no suprimir el bloque try catch, que en si mismo no genera ningún overhead, solo perdemos rendimiento cuando la excepción sucede, si no salta ninguna excepción la presencia o no de dicho bloque no afecta prácticamente al rendimiento.
En la entrada anterior comenzamos a ver las características de c# 3.0, concretamente la inclusión de los inicializadores de objeto y las propiedaes automáticas, que, como vimos, nos permitían mostrar nuestro código de una forma mucho más limpia.
En esta nueva entrega vamos a ver los metodos de extensión que al igual que lo anterior nos facilitarán la vida en ciertas tareas e incluso nos permitiran cambiar, en cierto sentido, nuestra forma de programar.
En términos generales los métodos de extensión se limitan a proporcionarnos la capacidad de extender una clase o interfaz ya existente añadiendole nuevos métodos, es decir, complementar esa clase con nueva funcionalidad (cosas que, por ejemplo, hemos tenido que tener en cuenta más tarde), aunque donde realmente consiguen su máxima potencia los métodos de extensión es en los interfaces y clases genéricos, y su mejor ejemplo (que ya veremos más adelante) es LinQ

Vale, se que esto viene un poco tarde puesto que lleva mucho tiempo en el mercado ya, y no digamos como beta, de hecho Monet, la librería de plugins que creé hace un tiempo, ya utiliza algunas de dichas características.
Visto por encima podemos decir que C# 3.0 proporciona los siguientes "avances" o "facilidades" nuevas:
En este primer post solo voy a cubrir las dos primeras partes, en un segundo hablaré de las extensiones y ya en el tercero os hablaré de las maravillas de las lambda expressions y el LinQ.
El atributo "Obsolete" nos permite marcar un elemento de código como obsoleto y asignar un comentario o mensaje de aviso en caso de que se utilice dicho elemento de código. Adicionalmente permite pasar un valor booleano que indique si el atributo se comportará como un error o como un warning.
El uso del atributo resulta útil conforme nuestro código va evolucionado. Quizá una función o una clase que diseñamos hace tiempo para una biblioteca ya no sea la forma más eficiente de hacer las cosas, de forma que queremos sustituir la función o la clase con otra nueva. En este caso el uso del atributo obsolete nos permite mantener la clase o función de forma que un desarrollador que estuviera utilizando la función no se encuentre con que ha desaparecido sin saber por qué sino con un warning (o un error en caso de que así lo hallamos definido) avisándole de que cambie y, preferiblemente, indicándole los pasos a realizar. Vamos a ver un ejemplo muy típico:
La librería reflection de .NET nos va a permitir acceder a la información de los atributos que hemos definido en el artículo anterior.
Reflection es el nombre dado por microsoft al conjunto de utilidades que permite leer la información y metainformación de las dll y ejecutables de .NET en tiempo de ejecución. Su utilidad fundamental está contenida en la biblioteca System.Reflection y contiene diversas funciones para cargar ensamblados, crear objetos, invocar métodos o leer información, todo ello en tiempo de ejecución.
Este artículo se centrará unicamente en el uso de .NET para leer la información de atributos, aunque se hará uso de otras funciones para acceder a las clases o los métodos involucrados. Para más información os recomiendo que leáis el artículo sobre .NET Reflection
El código y otra información adicional está disponible en la página del proyecto Monet Plugins Library y tenéis disponible la última versión del código en el control de versiones de subversion http://svn.thealphasite.org/Monet.
Una de las posibilidades que incluye .NET es el uso de atributos para proporcionar metainformación añadida a las distintas partes del código que creamos.
Los atributos de .NET nos proporcionan una poderosa herramienta para añadir información a nuestras clases, métodos, propiedades etc.
Un atributo puede aplicarse puede aplicarse sobre cualquier elemento básico de .NET es decir, clases, eventos, propiedades, métodos, etc y añadirá información a dicho elemento que posteriormente podremos consultar en tiempo de diseño, compilación o ejecución.
El uso de atributos nos permite acercar la programación en .NET al concepto de AOP, que son las siglas inglesas de Aspect Oriented Programing o Programación arientada a aspectos.
Para comprender correctamente en que consiste el paradigma de programación orientada a aspectos debemos partir de la base de las limitaciones de la programación orientada a objetos. En el paradigma orientado a objetos, el concepto de herencia describe las relaciones jerárquicas entre los distintos objetos que a su vez modelan el mundo real (o conceptos de este).
Un sistema de plugins es un mecanismo complejo. Por un lado el traslado de dependencias hacia el tiempo de ejecución (como veremos más adelante) lo hace mucho más complicado de explicar, de comprender y de implementar. Hay muchas cuestiones a tener en cuenta, este artículo trata de abarcar las principales pero me temo que hay muchas cuestiones que quedarán fuera.
Por otro lado, aunque para entender el sistema no es necesario un conocimiento de .NET o C#, desde luego estos puntos ayudarán a comprenderlo mejor y con más profundidad.
Evidentemente el sistema es demasiado complejo para que explique aquí todas y cada una de las partes que componen el sistema. El código en si, que incluyo en el artículo, está bastante comentado y he procurado ilustrar en el artículo las partes principales del sistema pero no me cabe duda de que habrá zonas que queden sin comentar.