Problemas de concurrencia

Why concurrent collections?

En un mundo en el que los procesos ya no son secuenciales sino paralelos, es cada vez más posible encontrarnos con problemas de concurrencia al acceder a recursos compartidos. Conceptualmente hablando, esto es algo a los que los desarrolladores ya estamos acostumbrados cuando trabajamos con gestores de bases de datos como Oracle o SQL Server, ya que varios usuarios pueden acceder o modificar la información al mismo.

Sin embargo, la gran mayoría de los desarrolladores pocas veces hemos tenido que lidiar con bloqueos en colecciones en menoria, ya que no todo el mundo crea aplicaciones en las que varios threads acceden a recursos compartidos. De hecho, si alguna vez has lo tenido que hacer sabrás perfectamente que antes de la aparición de la TPL era una de las disciplinas más complejas dentro del desarrollo de software. Algo que favorece la calvície :)

Sin embargo, desde la aparición de la TPL en el .NET Framework 4.0 es mucho más sencillo desarrollar aplicaciones que ejecuten procesos en paralelo o de forma asíncrona, pero esto conlleva que en ocasiones nos olvidemos que hay algunos threads que se ejecutan al mismo tiempo, y esto podría llevar a producir efectos no deseados cuando se trata de acceder a recursos compartidos, como una colección de elementos en memoria.

Por ejemplo: supongamos un escenario en el que tenemos una lista de clientes en memoria y un par de tareas (Task) que acceden a esa colección. La primera tarea podría estar verificando si todos los clientes cumplen una condición X, y si no la cumplen eliminar el cliente de la colección. Mientras tanto la segunda tarea podría estar actualizando alguna propiedad de los clientes, como la edad.

Un escenario real

Encierra este escenario algún peligro? A priori podemos pensar que no. Basta con que la segunda tarea verifique si el cliente existe en la colección antes de actualizarlo. Seguro? Pues no. Al menos no basta si la colección que estamos usando no es una de las nuevas definidas dentro del namespace System.Collections.Concurrent. Y para verlo mejor, hagamos un pequeño ejemplo con código, que es lo que nos gusta a todos.

1 – Partiremos de una clase base Customers con un método que crea n objetos de ejemplo:

public class Customer
{
    public int CustomerId { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }

    public static List<Customer> GetSampleCustomers(int n)
    {
        Random r = new Random(Guid.NewGuid().GetHashCode());
        List<Customer> customers = new List<Customer>();
        for (int i = 0; i < n; i++)
        {
            customers.Add(new Customer()
            {
                CustomerId = i,
                Name = string.Format("Customer {0}", i),
                Age = r.Next(20, 80)
            });
        }
        return customers;
    }
}

2 – A continuación en un formulario declararemos e inicializaremos una colección de tipo Dictionary para almacenar estos objetos cliente:

Dictionary<int, Customer> dic1 = new Dictionary<int,Customer>();

3 – Y en el constructor la rellenamos con 100.000 clientes de ejemplo:

public Form1()
{
    InitializeComponent();
    dic1 = Customer.GetSampleCustomers(100000).ToDictionary(p => p.CustomerId);
}

4 – Ahora, vamos a complicar un poco más el ejemplo y crearemos dos métodos que simulen el borrado y la actualización de los objetos que contiene la colección. Luego llamaremos a éstos métodos para 100 objetos aleatorios de forma paralela para ver que efectos se producen:

private void deleteItem1(int id)
{
    if (dic1.ContainsKey(id) && dic1[id].Age < 30) dic1.Remove(id);
}

private void updateItem1(int id)
{
    if (dic1.ContainsKey(id)) dic1[id].Age++;
}

private void button1_Click(object sender, EventArgs e)
{
    for (int i = 0; i < 100; i++)
    {
        Random r = new Random(Guid.NewGuid().GetHashCode());
        int id = r.Next(1, dic1.Count);
        Task.Factory.StartNew(() => deleteItem1(id));
        Task.Factory.StartNew(() => updateItem1(id));
    }
}

Ejecutamos la aplicación y cuendo pulsemos el botón puede pasar que obtengamos un bonito error como este:

image

Concretamente en la línea que incrementa la edad del cliente:

image

Y digo que puede pasar, pero no es seguro. Tal vez tengamos que repetir el proceso varias veces, y es que en un entorno en el que varios threads acceden a un recurso compartido nada nos asegura que se produzca el error. A veces pasa a la primera y a veces a la cuarta, pero hay que tener presente que cuando esté en producción siempre pasará en viernes por la tarde cinco minutos antes de empezar el fin de semana ;)

Analicemos el porqué del error, por que ¿cómo es posible que no encuentre el elemento en la colección si precisamente en la línea anterior verificamos su existencia? Pues ahí está la gracia, que lo verificamos en la línea anterior. A nosotros -pobres developers- nos parece que la línea anterior en la que verificamos si existe y la línea actual en la que lo emininamos van seguidas una detrás de otra, pero realmente en un entorno asíncrono hay todo un mundo entre ambas. Y eso es porque ambas líneas se ejecutan de forma separada, sin ningún tipo de bloqueo, no existe Transaccionalidad al estilo de las bases de datos, de modo que mientras el primer thread verifica que existe ese elemento en la colección y lo borra, llega el segundo thread y… ZASCA! Lo elimina.

image

El nuevo ConcurrentDictionary

Para estos casos (y muchos otros) aparecen en escena un nuevo conjunto de colecciones especializadas en entornos multithreading. Una de las más populares es una variación del clásico Dictionary, el ConcurrentDictionary. Esta clase proporciona una série de métodos alternativos para agregar, modificar o eliminar elementos llamados TryAdd, TryUpdate o TryRemove. La ventaja de éstos métodos es que son transaccionales y proporcionan Atomicidad. Es decir, garantizan que en caso de que se invoquen, se ejecuten totalmente: O bien se añade/modifica/elimina el elemento de la colección o bien se devuelve el valor False porque no se ha podido realizar la operación. También proporciona un método TryGetValue que intenta obtener un elemento de la colección. Todos estos métodos implementan en su interior bloqueos (lock), para asegurar que nadie más accede al elemento mientras se realiza la operación solicitada.

Un ejemplo real que no falla ;)

Vamos a modificar el ejemplo anterior usando esta nueva colección, para observar su comportamiento en el entorno anterior.

1 – Agregaremos un ConcurrentDictionary después de la declaración del anterior:

ConcurrentDictionary<int, Customer> dic2 =
    new ConcurrentDictionary<int, Customer>();

2 – Lo rellenamos en el constructor, al igual que hacíamos antes. Aquí podemos ver la primera diferencia, ya que usamos uno de los nuevos métodos TryXXX:

Customer.GetSampleCustomers(100000).ForEach(c => dic2.TryAdd(c.CustomerId, c));

3 – A continuación vamos a crear los métodos equivalentes para eliminar y modificar el elemento en la colección:

private void deleteItem2(int id)
{
    Customer c;
    if(!dic2.TryRemove(id, out c)) Console.WriteLine("Error deleting!");
}

private void updateItem2(int id)
{
    Customer c;
    if (!dic2.TryGetValue(id, out c))
    {
        Console.WriteLine("Error getting value");
    }
    else
    {
        Customer newc = new Customer() { CustomerId = c.CustomerId, Name = c.Name, Age = c.Age };
        newc.Age++;
        if (!dic2.TryUpdate(id, newc, c)) Console.WriteLine("Error updating!");
    }
}

En el borrado usamos el método TryRemove que necesita el id del elemento a eliminar, y en caso que lo elimine con éxito, devuelve True y el objeto eliminado en el segundo parámetro out (de salida) del método. En caso que no lo elimine devuelve False y el valor default del objeto a eliminar en el segundo parámetro.

La actualización es un poco más compleja, ya que primero debemos asegurarnos de que el elemento a modificar existe en la colección mediante el método TryGetValue, que básicamente funciona igual que el método TryDelete anterior. Una vez hemos comprobado que dicho objeto existe en la colección creamos una réplica del cliente, modificamos su edad y llamamos al método TryUpdate, el cual se encarga de la modificación.

Es interesante notar que para que la modificación se realice correctamente debemos informar de la clave del objeto en la colección, el nuevo valor del objeto (en este caso la réplica del cliente a la que hemos modificado la edad) y un tercer parámetro que es el valor original del objeto, que es usado para comparar.

4 – Probemos los nuevos métodos en otro botón:

private void button2_Click(object sender, EventArgs e)
{
    for (int i = 0; i < 100; i++)
    {
        Random r = new Random(Guid.NewGuid().GetHashCode());
        int id = r.Next(1, dic1.Count);
        Task.Factory.StartNew(() => deleteItem2(id));
        Task.Factory.StartNew(() => updateItem2(id));
    }
}

Ejecutamos y si vamos a la ventana de consola podremos observar el resultado:

Error getting value
Error getting value
Error getting value
Error getting value
Error updating!
Error getting value
Error getting value
Error getting value

En la mayoría de los casos, obtenemos un error de lectura porque el elemento ya ha sido eliminado de la colección, pero en algunos casos el error se producirá en el método TryUpdate. Eso significa que la llamada a TryGetValue encuentra el elemento, pero cuando lo vamos a modificar éste ya no existe en la colección. Perfecto! :)

Conclusión

La ventaja de usar colecciones específicas en entornos multithreading es clara: Garantizan que el acceso a los recursos compartidos sea transaccional, al más puro estilo de las bases de datos. De acuerdo que su programación sea un poco más compleja, pero no tiene nada que ver con lo que se tenía que hacer antiguamente cuando no existía la TPL.

Por otro lado, el uso de estas colecciones debe hacerse sólo en aquelos casos en los que se accede a recursos compartidos, ya que si no, por un lado estamos complicando nuestro codigo y por otro lado un ConcurrentDictionary no es tan eficiente como lo és un Dictionary, sobre todo en entornos más de escritura que de lectura. Tenéis un post muy detallado al respecto del gran James (aka Black Rabbit) respecto a la diferencia de performance:

http://geekswithblogs.net/BlackRabbitCoder/archive/2010/06/09/c-4-the-curious-concurrentdictionary.aspx

Espero sacar tiempo para otro post en el que explicar otras colecciones de este namespace, como las ‘BlockingCollection’ o las ‘’ConcurrentBag’, que son colecciones más especializadas. La primera está optimizada para escenarios dónde el mismo thread produce y consume objetos, mientras que la segunda ha sido especialmente diseñada para escenarios de productor-consumidor.

Aquí tenéis un pequeño proyecto de ejemplo con el código:

https://www.dropbox.com/s/ej3u3s1v55ltb30/TPL04_Concurrency.zip

Nos leemos! :D

Volver al índice de contenidos

Entrevista para el MVP Site (versión completa)

MVPPartySeattle2011

Ayer me publicaron una entrevista en el sitio internacional del programa MVP:

http://mvp.microsoft.com/es-ES/Paginas/default.aspx (spanish)

Sin embargo, no es la versión completa. Ésta la publico aquí, sin cortes. Espero que os guste ;)

Entrevista a Lluís Franco

Nacido en Barcelona y con 20 años de experiencia en el sector del desarrollo de software, Lluís Franco posee varias certificaciones MCP y MCTS y ha sido reconocido como Microsoft MVP durante los últimos 10 años, principalmente por haber sido colaborador activo y moderador en los grupos de noticias y en los foros MSDN de Microsoft desde 1997.

Actualmente trabaja como responsable de desarrollo de proyectos en FIMARGE una empresa del sector financiero situada en Andorra. En 2007 fundó AndorraDotNet, el primer grupo de usuarios y en 2010 la asociación virtual de informáticos del país. Escribe en varios blogs, y es organizador y ponente habitual en los eventos de los grupos de usuarios de la región. También colabora con un periódico local escribiendo artículos para su sección tecnológica.

En lo personal vive en Andorra desde hace más de una década, está felizmente casado con una mujer encantadora y es padre de las dos niñas más guapas del mundo.

1. Que significa ser MVP para ti?

Algo mucho más importante ahora que cuando recibí mi primera nominación, ya que por aquel entonces no era consciente de toda la gente que iba a conocer ni de todo lo que iba a suceder en los próximos 10 años. Y con esto no me refiero sólo al nivel técnico de este colectivo -que lo tiene, y mucho- sino a la calidad humana, porque a muchos de mis mejores amigos actuales los he conocido a través del programa MVP.

2. Cómo empezaste a colaborar en la comunidad?

Sinceramente? Pues de casualidad. La verdad es que como muchos otros profesionales, un día hace unos cuantos años necesitaba ayuda para resolver una duda, y buscando me encontré los ya desaparecidos grupos de usuarios, en los que me ayudaron a resolver la mayoría de mis dudas. Eso me encantó y poco a poco decidí devolver el buen trato recibido ayudando también a otros. Después empecé con una web, un blog, otro blog, a hablar en pequeños eventos, luego también a organizarlos… y mira. Hasta hoy :)

3. Si pudieras preguntarle a Steve Ballmer una pregunta, que te gustaría preguntar?

Ufff… lo primero que me viene a la cabeza es preguntarle por la estrategia a largo plazo de algunas de las líneas de negocio. Aunque pensándolo mejor, creo que todo el mundo debe preguntarle ese tipo de cosas, así que mejor le preguntaría por los viejos tiempos: Así que Steve, si me estás oyendo… ¿cuál crees que ha sido el hecho más importante en la historia de Microsoft? Me refiero a algún punto de inflexión que según tu opinión -y no la de un libro o película- sea lo que ha hecho de Microsoft lo que es hoy en día.

4. Cual crees que ha sido el mejor Software de la historia?

Soy un enamorado de C#. Para mí es, sin ningún lugar a dudas el lenguaje más bello, completo y potente jamás creado, así que por extensión VS2012 es el mejor IDE de la historia (o al menos de los que he usado, que son unos cuantos). En cuanto a sistemas operativos, para mí tampoco no hay duda: Windows 8 con mucha diferencia sobre el segundo. Y que conste que no siempre he trabajado con productos de Microsoft, y algunos me gustan mucho, pero a día de hoy ésta es mi opinión.

5. Si tú fueras el responsable de C# qué cambio te gustaría hacer?

Seguir implementando características de la programación funcional. En las últimas versiones, la aparición de genéricos, el manejo de delegados, LINQ y sobretodo las expresiones lambda han convertido C# en el lenguaje que siempre había soñado. ¡Créeme si te digo que hace muy divertido el desarrollar! Sin embargo todavía faltan cosas, y aunque no son imprescindibles (como las Nested functions) sí que harían -como dicen unos buenos amigos míos- que en C# las funciones fueran consideradas ciudadanos de primer nivel, y así dejarían de darme la paliza. Ah! Y permíteme agregar también otra cosa: Aligerar un poco el Framework y el IDE. Porque después de tantas versiones hay ciertos objetos y unas cuantas colecciones que deberían ser eliminadas… y perder unos Kb nunca viene mal.

6. Cuál fue el último libro que has leído?

Suelo ser un lector compulsivo de libros técnicos, fantasía y ciencia ficción, así que no esperes que te diga que un superventas. A ver, mi último libro ha sido ‘Ready Player One’, una novela llena de referencias de los años 80: Hay videojuegos clásicos, música, películas, rol, comics y hasta juguetes. Si bien el estilo es un poco juvenil, no deja de ser una perla para cualquier freak nacido en los 70, porque al fin y al cabo nos hemos criado en esa época. También suelo tener en mi mesilla de noche algún libro técnico del cuál ojeo fragmentos de vez en cuando y cómo no, mi biblia particular: El Silmarillion.

7. Que CD de música recomiendas?

Uno solo? Entonces supongo que me quedaría con el ‘Ok Computer’ de Radiohead. Lo encuentro un disco redondo dentro del género alternativo y como banda me encantan, sobre todo los primeros discos. Con todo, mi criterio musical va desde Bach hasta el buen Metal, pasando por mucho Jazz, así que no se si mi criterio es el más fiable.

8. Que hace que tú seas un excelente MVP?

No soy un excelente MVP, eso lo tengo clarísimo. Verás, déjame matizar lo que para mí significa ser un MVP: Un profesional con un cierto nivel técnico pero que destaca por querer contribuir a la comunidad, ayudando a otros como antes lo ayudaron a él. Y para eso la actitud es básica, y debe ser más importante que las cualidades técnicas. De modo que si alguna vez veis a alguien decir “Ves? Soy mejor que tú porque soy MVP”, pensar que esta persona no merece formar parte del programa. Es más, tenéis mis datos arriba. Poneros en contacto conmigo, ok? No bromeo.

9. Que contiene tu “Computer Bag”?

Cada vez menos software y más hierro. Y con la proliferación de pequeños dispositivos cada vez llevo más adaptadores, cargadores, conectores universales para los viajes y por supuesto una reliquia de mis primeros días de MVP: Una navaja suiza con el logo del programa MVP que contiene un montón de herramientas. No la he usado nunca, pero no pierdo la esperanza.

10. Cuál es la mejor cosa que te ha pasado desde que eres MVP?

La fama, las mujeres, el dinero… es difícil. Podría decirte que ser escogido como C# MVP of the year, ya que es un premio que se otorga una vez ya eres MVP por parte de tus propios compañeros. O mi primer viaje a Seattle, que también fue algo apoteósico. Pero si tengo que escoger una única cosa me quedo con la gente. Ya lo he dicho antes, pero me ha permitido conocer profesionales como la copa de un pino, que además son gente muy maja y dispuesta a ayudar a la comunidad sin reservas. Y lo mejor de todo es que la gran mayoría son unos geeks como yo. No hay nada como un buen geeks & beers…

11. Cuál es tu lema?

Me encantaría poder soltaros una frase molona, pero la verdad es que mi lema es bastante corriente: Intentar ser mejor persona y alguien en quién mis dos hijas se puedan identificar cuando crezcan. No pretendo cambiar la sociedad, con lo anterior ya tengo trabajo de sobras.

12. Quien es tu héroe?

Sheldon Cooper. Yoda. Spock. Frodo. Cálico. Fuckowsky de ‘Memorias de un ingeniero’, y algún otro que se me olvida. Pero supongo que si debo elegir me quedo con Tony Stark: Un tío que es un genio en varios campos, playboy y que viste su propia armadura de combate… ¡Venga ya! ¿A quién no le gustaría? Sin embargo en el mundo real un héroe es algo mucho más difícil: Alguien que hace lo correcto a pesar de complicadas que están las cosas y sin que nadie lo obligue, un niño con una enfermedad grave dando ánimos a sus padres… Eso si que son héroes!

13. Que significa el éxito para ti?

¡Trabajar de algo que te gusta! Fíjate que en realidad son dos cosas: La primera trabajar, que con la que está cayendo por todos lados, es casi un lujo. Y la segunda, encima, hacer algo que realmente te apasiona. Porque al fin y al cabo es algo que vas a tener que hacer durante muuuuchos años, y si no te apasiona estás perdido, ya que la pasión es el motor que va a hacer que evoluciones con los años. Intentad no perderla!

14. Mencione un proyecto que desearía cumplir este año.

Ya he ido a Seattle al MVP Summit, y también he organizado el primer Geek-a-paloozaaa en Andorra, así que a nivel de trabajo me gustaría terminar una larga serie de posts y videos sobre programación paralela que empecé hace unos meses y la he pausado temporalmente por falta de tiempo. He recibido mucho feedback pidiéndome que la continúe, y eso es lo que voy a hacer. Por otro lado a nivel más personal me gustaría no trabajar tanto, pasar más tiempo con mis seres queridos y no ser tan gruñón a veces. Por pedir que no quede!

Nos vemos! :-D

MVP por décimo año (Frodo, voy a por tí)

MVP_BlueOnly

:-)
Hola a todo el mundo!

Pues eso, que me acaba de llegar EL CORREO. Y a diferencia de otros años, la verdad es que no me acordaba de que hoy era el día D, el día en el que se nombran o renuevan a los MVPs de Octubre… y la verdad es que me he ahorrado algunos nervios. Bueno, al menos hasta hace 2 ó 3 horas, porque la inefable Pilar (que también es de Octubre) me lo ha recordado… Gracias por los nervios! Te debo una! :D

Sin embargo, al final ha llegado:

Estimado/a Lluis Franco i Montanyes,
Enhorabuena. Nos complace presentarle el programa de nombramiento MVP de Microsoft® de 2012. Este nombramiento se concede a los líderes excepcionales de la comunidad técnica que comparten de forma activa su experiencia de alta calidad y de la vida real con otras personas. Le agradecemos especialmente la contribución que ha realizado en las comunidades técnicas en el área de Visual C# a lo largo del pasado año.

La verdad es que este año me hace especial ilusión porque se trata de mi 10º año perteneciendo al programa, y caramba, y eso es una jartá de tiempo! Además, este año toca un anillo especial (como cada 5 años) así que cuando llegue ya os pondré una foto… aunque yo hubiese preferido un jamón ;)

No quiero ponerme sentimental, pero si quiero agradecer a quién sea (nadie sabe exactamente como funciona esto de las nominaciones) el que hay pensado que  merezco pertenecer a este grupo de monstruos. Y quiero decirlo así: Monstruos, porque la gente que pertenece al programa son unos verdaderos cracks técnicos, pero además -salvo raras excepciones- son gente realmente cojonuda. Lo que se llama buena gente, vamos… No es extraño que a muchos de mis mejores amigos los haya conocido aquí.

Dicho esto, como no quiero hacer un post que sea un tocho, os dejo algunos enlaces a los posts de antiguas renovaciones, en los que podréis ver desde fotos de lo mal que lo pasamos cuando nos juntamos, hasta algún video de los bautizos de los nuevos.

MVP Award 2011

MVP Award 2010

MVP Award 2009

MVP Award 2008

Muchas gracias a todos! :D

Parallel Series: Tasks, la 8ª maravilla

Nota: Antes de nada quiero disculparme por haber dejado sin publicar en esta serie durante tanto tiempo, de hecho casi un año. Afortunadamente no ha sido ningún problema de salud, si no el tener demasiados frentes abiertos. Ahora que parece que se van cerrando algunos pienso aprovechar para terminar la serie y embarcarme en algún otro nuevo que ya os contaré.

The show must go on

En los posts previos hemos hablado de PLINQ y de la clase Parallel, dos características que facilitan mucho la ejecución de datos en paralelo, sin embargo si alguien me preguntase cual es la característica que más me gusta de la Task Parallel Library lo tendría muy claro: El manejo de tareas asíncronas mediante la clase Task.

Durante muchos años los desarrolladores hemos tenido que lidiar -más ben pelearnos- con la multitarea y la ejecución de código asíncrono. Ya dese las primeras versiones de C# podíamos crear hilos a mano mediante la clase Thread, pero el proceso distaba mucho de ser sencillo y además adolecía de cierta complejidad para manejar cancelaciones o actualizar la interfaz de usuario. Y aunque han habido intentos de mejora como la interfaz IAsyncResult también han aparecido engendros infumables como el BackgroundWorker, el cual es tan malo como hacerse cirugía cerebral con manoplas uno mismo.

Task al rescate!

Así que cuando apareció la TPL y la clase Task los desarrolladores encontramos por fin un método simple y cómodo para ejecutar código asíncrono y además en paralelo. Y esto es realmente muy importante ya que hoy en día en muchas aplicaciones (al menos las que están bien diseñadas) se ejecutan tareas en paralelo para acceder a recursos externos o ‘costosos’, bases de datos, y sobre todo para actualizaciones de la interfaz de usuario. De hecho es tan importante que será una de las mejoras más importantes en la siguiente versión de C# 5.0 de la cual hablaremos al final de la serie.

TaskClass

Actions everywhere

Al igual que la clase estática Parallel y gran parte de la TPL, la clase Task se basa en acciones, de modo que si no las controlas demasiado dale una ojeada al post que publiqué hace un tiempo sobre el tema.

En su sintaxis más básica, se puede utilizar de este modo:

var t = new Task(() => {
    Thread.Sleep(1000);
    Console.WriteLine("A");
    });
t.Start();
Console.WriteLine("B");

O lo que es lo mismo:

Task.Factory.StartNew(() => {
    Thread.Sleep(1000);
    Console.WriteLine("A");
    });
Console.WriteLine("B");

La única diferencia es que en la primera declaramos la variable especificando la acción a ejecutar y luego la ejecutamos explícitamente mediante su método ‘Start’, y en la segunda no utilizamos ninguna variable, sólo especificamos y ejecutamos la acción mediante el método ‘StartNew’ de la clase ‘Task.Factory’.

Observando el código anterior, qué os pensáis que se escribirá primero? A o B? Evidentemente B, ya que podemos imaginar cómo el código no se detiene en el Thread.Sleep(1000) (éste se ejecuta en otro Thread) de modo que ejecuta inmediatamente el print ‘B’. Correcto.

Pero las tareas son mucho más, permiten desde devolver resultados hasta manejar éstas ‘unidades de trabajo’ encadenando tareas a continuación de otras, esperando a que terminen una o un grupo de tareas antes de ejecutar otra, cancelar una tarea o propagar excepciones entre ellas.

1) Devolver un valor desde una tarea…

Como en un método, una tarea puede devolver desde un tipo básico (int, string) hasta cualquier tipo complejo. Para hacer el ejemplo más interesante vamos a utilizar un método que examina la red local en busca de servidores SQL Server y devuelve una lista de strings:

public static List<string> GetSQLServerNames()
{
    List<string> sqlservernames = new List<string>();
    sqlservernames.Add("local");
    SqlDataSourceEnumerator enumSQLServers = SqlDataSourceEnumerator.Instance;
    foreach (DataRow server in enumSQLServers.GetDataSources().Rows)
    {
        if (server["InstanceName"] is System.DBNull)
            sqlservernames.Add(server["ServerName"].ToString());
        else
            sqlservernames.Add(
                string.Format("{0}\\{1}", server["ServerName"], server["InstanceName"]));
    }
    Console.WriteLine("end get servers");
    return sqlservernames;
}

La ventaja de usar este método es que tarda unos segundos en ejecutarse, con lo cual es un candidato perfecto para ser ejecutado de forma asíncrona:

var getServersAsync = new Task<List<string>>(() => GetSQLServerNames());
getServersAsync.Start();
Console.WriteLine("end call");

Si lanzamos este código observaremos que se imprime ‘end call’ inmediatamente, y tarda unos segundos en imprimir ‘end get servers’. Realmente se está ejecutando asíncronamente!

2) …y al terminar continuar con otra tarea

Ahora supongamos que tenemos otro método que se encarga de actualizar la interfaz de usuario  a partir de la lista anterior:

private void updateServersListUI(List<string> servers)
{
    comboBox1.Items.Clear();
    servers.ForEach(p => comboBox1.Items.Add(p));
}

¿No sería lógico que lo hiciese al terminar la tarea anterior? Pues la verdad es que si, y encadenar tareas es algo trivial y que ofrece mucha potencia al desarrollador. De hecho estoy seguro que ya se os ha ocurrido alguna aplicación ;)

Encadenar tareas es tan sencillo como utilizar el método ‘ContinueWith’:

var getServersAsync = new Task<List<string>>(() => GetSQLServerNames());
getServersAsync.Start();
Console.WriteLine("end button2");
getServersAsync.ContinueWith((p) => updateServersListUI(getServersAsync.Result));

Sobre el papel debería funcionar pero no lo va a hacer, ya que hasta ahora no nos hemos fijado en un detalle: En la plataforma .NET no es posible actualizar un control desde otro hilo distinto al que lo ha creado, y toda la interfaz de usuario se crea en el main thread o hilo principal. Y esta limitación todavía existe.

Estableciendo el contexto de ejecución

Antes de la TPL para actualizar la interfaz de usuario desde otro hilo debíamos utilizar el método ‘Invoke’ de la clase ‘Control’, de modo que deberíamos modificar el método anterior de este modo:

private void updateServersListUI(List<string> servers)
{
    if (this.InvokeRequired) this.Invoke(new Action(() =>
    {
        comboBox1.Items.Clear();
        servers.ForEach(p => comboBox1.Items.Add(p));
    }));
}

Pero no va a ser necesario, ya que como parte de la magia de la TPL se nos ofrece la posibilidad de llamar a ‘TaskScheduler.FromCurrentSynchronizationContext’ que nos permite acceder a la interfaz de usuario de forma segura. Así pues lo único que hay que modificar el código anterior es encadenar la segunda tarea usando el contexto de sincronización antes mencionado y olvidarnos de la llamada a Invoke:

getServersAsync.ContinueWith((p) => updateServersListUI(getServersAsync.Result),
TaskScheduler.FromCurrentSynchronizationContext());

Esperando la ejecución de varias tareas

Otra característica muy interesante es la posibilidad de esperar a que termine un grupo de tareas entero, o sólo una de ellas. Supongamos que tenemos unja serie de tareas que se encargan de aplicar unos efectos a una serie de imágenes:

var t1 = Task.Factory.StartNew(
    () => pictureBox1.Image = Properties.Resources.Landscape08.Invert());
var t2 = Task.Factory.StartNew(
    () => pictureBox2.Image = Properties.Resources.Landscape08.Grayscale());
var t3 = Task.Factory.StartNew(
    () => pictureBox3.Image = Properties.Resources.Landscape08.Brightness(140));
var t4 = Task.Factory.StartNew(
    () => pictureBox4.Image = Properties.Resources.Landscape08.Contrast(80));
var t5 = Task.Factory.StartNew(
    () => pictureBox5.Image = Properties.Resources.Landscape08.Gamma(1, 5, 1));
var t6 = Task.Factory.StartNew(
    () => pictureBox6.Image = Properties.Resources.Landscape08.Color(255, 0, 0));

Podría ser interesante no seguir ejecutando el código hasta que la primera termine, o hasta que terminen todas, o hasta que terminen todas pero con un timeout. O sea, que si no terminan todas en 100 milisegundos seguir con la ejecución:

Task.WaitAny(new Task[] {t1, t2, t3, t4, t5, t6});
Task.WaitAll(new Task[] {t1, t2, t3, t4, t5, t6});
Task.WaitAll(new Task[] {t1, t2, t3, t4, t5, t6}, 100);

Otra forma de conseguir esto es mediante la creación de una tarea, especificando que debe esperar a que se complete alguna tarea o todas las especificadas:

var t7 = Task.Factory.ContinueWhenAll(new[] { t1, t2, t3, t4, t5, t6 }, (t) =>
    {
    //DoSomething...
    });

Cancelando tareas

Al igual que en posts anteriores, la clase Task también admite cancelaciones, y a mi juicio suelen ser más utilizadas que sus equivalentes en PLINQ o Parallel, ya que pueden permitir a un usuario cancelar el acceso a un recurso que tarda más de lo previsto (una URL p.e.) y las tareas que debían ejecutarse a continuación.

Partiendo de la base de un método que hace un trabajo largo:

private void DoALongWork(CancellationTokenSource cs)
{
    try
    {
        for (int i = 0; i < 100; i++)
        {
            Thread.Sleep(10);
            cs.Token.ThrowIfCancellationRequested();
        }
    }
    catch (OperationCanceledException ex)
    {
        Console.WriteLine(ex.Message);
    }
}

Podemos cancelar la tarea llamando al método ‘Cancel’ del token:

var cs = new CancellationTokenSource();
var t = Task.Factory.StartNew(
    () => DoALongWork(cs)
    );
Thread.Sleep(500);
cs.Cancel();

Y al cancelarse el código entrará en el catch del método ‘DoALongWork’ en el que se realizarán las acciones apropiadas a la cancelación.

Cancelación y estado

Para terminar, en algunos casos puede ser interesante que en caso de éxito la tarea llame a una segunda, pero en cambio si se cancela llame a una tercera. Para poder hacer esto debemos preguntar por el estado de la primera tarea y realizar un pequeño truco: La excepción no debe ser interceptada en un bloque try!

Así pues el código del método quedaría de este modo (sin try):

private void DoALongWork(CancellationTokenSource cs)
{
    for (int i = 0; i < 100; i++)
    {
        Thread.Sleep(10);
        cs.Token.ThrowIfCancellationRequested();
    }
}

Y la llamada de este otro:

var cs = new CancellationTokenSource();
var t = Task.Factory.StartNew(
    () => DoALongWork(cs)
    );
Thread.Sleep(500);
cs.Cancel();

t.ContinueWith(task =>
    {
        switch (task.Status)
        {
            case TaskStatus.Faulted:
                MessageBox.Show("Fail");
                break;
            case TaskStatus.RanToCompletion:
                MessageBox.Show("Success");
                break;
        }
    }, TaskScheduler.FromCurrentSynchronizationContext());

Como podéis observar, podemos cambiar el flujo del código en función del estado de la tarea.

Importante: Para poder ejecutar este último ejemplo es necesario ejecutar sin depuración (Ctrl+F5)

Bueno, nos hemos dejado algunas cosas en el tintero, pero para no hacer muy largo éste post las veremos más adelante, en otros posts avanzados de la misma serie.

Hasta entonces prometo actualizar la serie con más frecuencia que hasta ahora ;)

Volver al índice de contenidos

Tienes Suscripción MSDN? Pues a publicar en la Store!

Good news!

Acabo de enterarme en tweetland gracias al colega @wasat de que todos aquellos poseedores de una suscripción MSDN (y no me refiero sólo a la ultimate, si no a todas aquellas que incluyen Visual Studio) también disponen de un beneficio adicional:

Se trata de una cuenta para desarrollar aplicaciones y publicarlas en la Windows Store, además de la ya conocida cuenta para publicar aplicaciones para Windows Phone 7. WOW!

msdn01

De este modo, todos aquellos que hemos empezado a hacer nuestras primeras aplicaciones lo tenemos más facil que nunca. Basta con ir al perfil de vuestra suscripción (‘Mi cuenta’ en el menú superior) y verificar si disponéis de la opción Cuenta para desarrolladores de Windows Store, en la que podreis solicitar un código de registro.

msdn_store_account

A partir de aquí, os recomiendo un vistazo a este artículo en el que indicaa cómo registrar el código en la store y aclara algunos de los términos sobre el uso del código de registro, como por ejemplo que de momento sólo se pueden registrar empresas y no desarrolladores:

En este momento solo se pueden registrar cuentas del tipo Empresa. No se podrá completar el registro si selecciona Persona como tipo de cuenta.

Peeeeero que esto no os frene ni un momento… me ha dicho un pajarito que si tenéis lista vuestra aplicación para Windows 8 os podéis poner en contacto con el propio José Bonnin (@wasat) o con la cuenta oficial de MSDN España (@esmsdn) y os ayudarán a pasar un excelence lab y publicar vuestra aplicación en la store.

Así que… Espartanos! A desarrollar! :-D

Nota para Microsoft MVPS: Supongo que todos sabéis que uno de los beneficios de pertenecer al programa es una suscripción Visual Studio Ultimate with MSDN, así que chavales… let’s dev! ;)

Este es el manifiesto de los Free Radicals.

free-radical

Hacemos trabajo que es, ante todo, reconfortante. Pero cuando hacemos algo esperamos una validación extrínseca. No creamos solo para nosotros mismos. Queremos conseguir un impacto real y duradero en el mundo que nos rodea.

Pedimos libertad, tanto si trabajamos en compañías o por nuestra cuenta, para experimentar, participar en múltiples proyectos a la vez y desarrollar nuestras ideas. Avanzamos dentro de la flexibilidad y somos más productivos cuando nos sentimos totalmente comprometidos.

Hacemos muchos proyectos y, por lo tanto, cometemos errores a menudo. Vemos los pequeños fallos como oportunidades de aprendizaje y parte de nuestra educación experimental.

Tenemos poca tolerancia con las fricciones de la burocracia, redes de influencia de vieja escuela y las prácticas de negocio anticuadas. A menudo cuestionamos los procedimientos estándar y nos hacemos valer. Pero incluso cuando no podemos, no nos rendimos al status quo. En su lugar, encontramos fórmulas más interesantes a nuestro alrededor.

Esperamos que las compañías saquen lo mejor de nuestro trabajo y talento, ya sea una start-up o una gran compañía. Cuando nuestros proyectos y aprendizajes se estancan, nos vamos. Pero cuando podemos producir un impacto en algo que nos interesa, ¡estamos encantados! Queremos hacer nuestro trabajo siempre lo mejor que podemos.

Consideramos el open source, las API y el inmenso conocimiento colectivo de internet nuestro arsenal personal. Wikipedia, Quora y las comunidades abiertas para diseñadores, desarrolladores y pensadores fueron creadas por nosotros y para nosotros. Siempre que sea posible, difundimos conocimiento colectivo para ayudar a tomar mejores decisiones para nosotros mismos y nuestros clientes.

Pensamos que “networking” es compartir. Al compartir nuestras creaciones y las cosas que nos fascinan, creamos una comunidad de seguidores que nos dan feedback, ánimo y nos conducen hacia nuevas oportunidades. Por este motivo (y otros más), a menudo (no siempre) optamos por la transparencia sobre la privacidad.

Creemos en la meritocracia y el poder de las redes online para mejorar las habilidades que nos permiten hacer lo que nos gusta. Vemos la competencia como una motivación positiva más que como una amenaza.

Nos ganamos muy bien la vida haciendo lo que nos gusta. Nos consideramos, a la vez, artesanos y empresarios. A menudo somos nuestros propios departamentos de contabilidad, nuestra agencia de publicidad, nuestro asesor de desarrollo de negocio y nuestro comercial. Dedicamos la energía necesaria para invertir en nosotros mismos como negocio y empleamos las mejores herramientas y conocimientos (la mayoría son libres y online) para gestionar nuestro trabajo como una empresa de hoy.

Vía: YOROKUBU

Alucina: Premiado C# MVP of the year!

mvplogohor

Sorprendido. Perplejo. Flipado. Esas son ahora mismo las palabras que mejor pueden definir mi estado actual.

No obstante, a medida que voy escribiendo estas líneas y lo voy realizando voy experimentando una alegría enorme, que ya está se empezando a transformar en una sonrisa beatífica, por no decir estúpida. Y es que he recibido un mail de Lisa Feigenbaum (Microsoft Community Program Manager) con un mensaje que dice:

“Congratulations on being awarded C# MVP of the Year based on your contributions in 2011!”

WOW! Mola mucho, aunque la verdad y aunque suene a tópico no me lo esperaba ni de coña…

Durante los últimos 9 años he tenido la suerte de ser reconocido como Microsoft MVP y eso es algo que valoro mucho. No tanto por ser reconocido como ‘experto’ (cuesta mucho emplear esa palabra habiendo gente tan condenadamente buena), sino por el hecho que se reconozca la labor de ayudar a la comunidad. Que al fin y al cabo es lo que debería contar en el programa MVP, y -seamos sinceros- no siempre es lo único que cuenta.

Pues bien, ahora resulta que en una votación entre los 234 compañeros MVP de la categoría de C# repartidos por todo el mundo y el equipo de producto han decidido nombrarme MVP de C# del año, sea lo que sea eso, que la verdad, todavía no lo tengo muy claro.

De entrada el próximo 2 de Marzo, hay una cena en Seattle con S. Somasegar (“Soma”), Senior Vice President de la división de desarrollo en Microsoft, así como otros peces gordos y miembros destacados de la comunidad. Para mi desgracia, no voy a poder asistir pues vuelvo de Seattle en mismo día por la mañana pero durante mi estancia en el campus de Microsoft trataré de al menos tomarme una cerveza con alguno de estos personajes :-)

Desde aquí quiero dar la enhorabuena al resto de premiados (he contado 29 en todas las categorías) y a todos mis compañeros. De verdad gente, sois grandes! :-D

Un abrazo a todos,

PD – La semana que viene ya os empezaré a contar mis batallitas en Seattle, en directo.