Concurrent programming and Managed Extensibilty Framework

March 4, 2010 17:59 por Luis Guerrero

Este últimos mes he estado dando un par de charlas sobre programación concurrente en .NET Framework con Task Parallel Library y MEF (Managed Extensibility Framework) Framework de extensibilidad administrada, así que aquí tenéis todos los recursos, la presentación y las demos.

image

Task Parallel Lirarty demos in Visual Studio 2010 RC format

No hay demos de MEF pero te puedes bajar algunas de aquí http://www.codeplex.com/mef/


Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Escribiendo código concurrente de alto rendimiento con monitores

November 30, 2009 01:53 por Luis Guerrero

Las aplicaciones multi hilo o concurrentes van a ser el siguiente gran problema para los desarrolladores, y tenemos que estar listos para este cambio tan grade. En el .NET Framework 4, Microsoft incluye una serie de nuevas APIs que ayudan al desarrollador en la creación de aplicaciones concurrentes. Eso no significa que tengamos que despreocuparnos del código concurrente sino que a partir de ahora va a ser más fácil hacerlo.

Actualmente estoy trabajando en una aplicación que hace un uso intensivo de la API de TPL. Básicamente la aplicación genera una serie de Task que administran la ejecución de una serie de reglas aplicadas a una Uri. Además de eso mantengo una lista con la colección de Task creadas por mi aplicación, para contabilizar cuantas están ejecutándose y una serie de estadísticas de velocidad de la aplicación.

Esto significa que tenemos una UI donde vamos mostrando los datos, creada en WPF 4 que necesita comunicarse con este código para obtener todas las tareas que se están ejecutando en ese momento. Teniendo en cuenta que estoy usando una simple List<Tast> para guardar la referencia de todas las tareas, necesito una manera de bloquear la lsita para añadir elementos a la lista, enumerar la lista y eliminar elementos. Pero queiro que este bloqueo dure lo menos posible para hacer que mi aplicación se ejecute lo más rápido posible y además quiero solo bloquear para las operaciones importantes añadir y eliminar tareas

Así que tengo dos operaciones importantes, crear una nueva tarea y añadirla a la lista, y cuando la tarea termina eliminar esa tarea del servidor, pero tengo que tener en cuenta que múltiples tareas pueden estar ejecutándose a la misma vez.

La creación de tareas es responsabilidad de un temporizador que está configurado para ejecutarse para Segundo y si el número de tareas es menor que el número de tareas mínimas genera más tareas y cada tarea creada la añade al sistema. Teniendo en cuenta que este temporizador es ejecutado cada segundo, necesito bloquear mi lista, hacer mi trabajo y después desbloquear. Teniendo en cuenta que el bloqueo para acceder a la lista puede ser mayor que el tiempo del temporizador porque otros threads pueden estar usando la lista necesito establecer un tiempo máximo de bloqueo porque si no lo que conseguiré será una clásico convoy de bloqueos en mi aplicación.

Teniendo en cuenta esto dentro del método de mi temporizador utilizo la nueva API de Monitor.TryEnter de .NET 4 que intenta, para un número de milisegundos, adquirir el bloqueo exclusivo y atómicamente establecer un valor que indica si el bloqueo se ha obtenido o no. En mi caso he establecido el temporizador en 400ms, porque en mi método hago más cosas que trabajar con esa lista.

bool taken = false;
try
{
    Monitor.TryEnter(task, 400, ref taken);
    if (taken)
    {
        if (!cancelationTokenSource.IsCancellationRequested)
        {
            var count = (from p in task
                         where p.Status == TaskStatus.Running || p.Status == TaskStatus.WaitingToRun
                         select p).Count();

        }
    }
}
finally
{
    if (taken)
    {
        Monitor.Exit(task);
    }
}

Como se puede observar en el código estoy llamando a la función de Monitor.TryEnter dentro de un bloque de try/catch, y en el bloque de finllay si el bloqueo se ha adquirido lo libero llamando a la función Monitor.Exit. Lo hago de esta manera para asegurarme que si durante la ejecución de mi código se lanza una excepción el código de finally siempre será ejecutado y el bloqueo liberado. Si no lo hiciese de esta manera podría causar que se nunca se liberase el bloqueo y tener un deadlock.

El otro escenario donde trabajo con la lista es para obtener el número de tareas ejecutándose, y hago lo mismo dentro de accesor get de una propiedad, pero esta vez lo tengo configurado a 250ms. Si el bloqueo se adquiere satisfactoriamente, lo que hago es copiar la lista en una lista nueva par después de manera segura trabajar sobre esa nueva lista.

public List<Task> Tasks
{
    get
    {
        bool taken = false;
        List<Task> list = null;
        try
        {
            Monitor.TryEnter(task, 250, ref taken);
            if (taken)
            {
                list = new List<Task>(task.ToArray());
            }
        }
        finally
        {
            if (taken)
            {
                Monitor.Exit(task);                         
            }
        }
        return list;
    }
}

Esta solución es simple y mantiene tu código con la tasa de contención más baja posible, pues no esperas infinitamente en todos los casos, solo en los más importantes.

A parte de esto podría haber hecho un wrapper de List<T> con ReaderWriterLockSlim para controlar las operaciones de lectura y escritura durante el bloqueo, pero ReaderWriterLockSlim está pensado para trabajar con varios lectores y solo un escritor y en mi aplicación tengo múltiples escritores y solo un lector.

Saludos.

Luis.


Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Silverlight, un mejor manejo de los servicios

November 13, 2009 18:25 por Luis Guerrero

Como todos es sabido Silverlight trae soporte para invocar servicios web, tanto servicios web tradicionales de .net como servicios de WCF. En esto últimos solo con soporte para httpBasicBinding.

Cada vez que generamos un proxy en Visual Studio para un proyecto de Silverlight, el proxy generado solo soporta invocaciones asíncronas. No podemos de ninguna manera hacer una invocación síncrona a un servicio web y esperar a la respuesta. Puede haber muchas maneras por las cuales Microsoft implemento este comportamiento predeterminado, pero creo que el más importante de todos es hacer que la UI de las aplicaciones de Silverlight *nunca* dejen de responder.

Así que puestos en antecedentes, podemos ahora estudiar nuestro problema e intentar mitigarlo. Cuando generamos un proxy en Visual Studio, lo que obtenemos es una función con el mismo nombre que la operación del servicio + “Async” y un evento que es el nombre de la operación + “Completed” en la que en los argumentos de respuesta tenemos el resultado de la invocación del servicio.

Algo como esto:

public partial class TraditionalServices : UserControl
{
    private ServiceClient service;
    public TraditionalServices()
    {
        InitializeComponent();

        service = new ServiceClient();
        service.GetDateCompleted += new EventHandler<GetDateCompletedEventArgs>(OnGetDateCompleted);
        service.GetEmployeeCompleted += new EventHandler<GetEmployeeCompletedEventArgs>(OnGetEmployeeCompleted);


        Loaded += new RoutedEventHandler(OnLoaded);
    }

    void OnLoaded(object sender, RoutedEventArgs e)
    {
        service.GetDateAsync();
    }

    void OnGetEmployeeCompleted(object sender, GetEmployeeCompletedEventArgs e)
    {
        
    }

    void OnGetDateCompleted(object sender, GetDateCompletedEventArgs e)
    {
        
    }
}

Conforme uno empieza a trabajar con los servicios de Silverlight encuentra que el código se puede volver un poco complicado puesto que no podemos invocar al servicio y después trabajar directamente con el resultado sino que lo tenemos que hacer en el método que se llamará cuando se termine la invocación del servicio.

Lo que os propongo en este artículo es que construyáis una clase Helper que os ayude a lidiar con estos problemas y hacer así el código un poco más claro.

Del código anterior para realizar la invocación a un servicio pasamos a esta:

service.GetDateTime(value =>
{
    string currentDateTime = value.ToString();
});

Como se puede apreciar el resultado es mucho mejor que la manera tradicional de trabajar con los proxies además de que el código queda mucho más claro.

¿Cómo está este helper implementado?

La idea es usar expresiones Lambda para hacer el código más legible, lo único que necesitamos es un delegado de tipo Action<T> en el que T será del mismo tipo del resultado de nuestra invocación al servicio web.

public void GetDateTime(Action<DateTime> value)
{
    service.GetDateAsync(value);
}

Con este simple patrón podemos simplificar las llamadas al servicio web y esconder su complejidad.

public partial class MainPage : UserControl
{
Helpers.ServiceHelper service;
public MainPage()
{
   InitializeComponent();

   service = Helpers.ServiceHelper.Instance;


   service.GetDateTime(value =>
   {
       string currentDateTime = value.ToString();
   });


   service.GetEmployee(value =>
   {
       ListBox listbox = new ListBox();
       listbox.ItemsSource = value;
       LayoutRoot.Children.Add(listbox);
   });

   service.DoStuff(() =>
   {
       // do stuff done
   });
}
}

Lo que hace la clase Helper es, cada vez que se realiza una invocación del servicio web se pasa por parámetro una referencia del delegado de tipo Action<T> que contiene la función que se llamará cuando termine la invocación. Una vez que termina la invocación del servicio lo que hacemos es obtener ese valor de vuelta y invocar al delegado con el resultado que es del mismo tipo que la signatura del delegado Action<T>.

public class ServiceHelper
{
private static ServiceHelper instance = new ServiceHelper();
public static ServiceHelper Instance { get { return instance; } }

private ServiceClient service;

private ServiceHelper()
{
   service = new ServiceClient();
   service.GetDateCompleted += new EventHandler<GetDateCompletedEventArgs>(OnGetDateCompleted);
   service.GetEmployeeCompleted += new EventHandler<GetEmployeeCompletedEventArgs>(OnGetEmployeeCompleted);
   service.AddCompleted += new EventHandler<AddCompletedEventArgs>(OnAddCompleted);
   service.DoStuffCompleted += new EventHandler<System.ComponentModel.AsyncCompletedEventArgs>(OnDoStuffCompleted);
}

void OnDoStuffCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
   if (e.Error == null)
   {
       Action action = (Action)e.UserState;
       action();
   }
}

void OnAddCompleted(object sender, AddCompletedEventArgs e)
{
   if (e.Error == null)
   {
       Action<int> action = (Action<int>)e.UserState;
       action(e.Result);
   }
}

void OnGetEmployeeCompleted(object sender, GetEmployeeCompletedEventArgs e)
{
   if (e.Error == null)
   {
       Action<ObservableCollection<Employee>> action = (Action<ObservableCollection<Employee>>)e.UserState;
       action(e.Result);
   }
}

void OnGetDateCompleted(object sender, GetDateCompletedEventArgs e)
{
   if (e.Error == null)
   {
       Action<DateTime> action = (Action<DateTime>)e.UserState;
       action(e.Result);
   }
}

public void GetDateTime(Action<DateTime> value)
{
   service.GetDateAsync(value);
}

public void GetEmployee(Action<ObservableCollection<Employee>> value)
{
   service.GetEmployeeAsync(value);
}

public void Add(AddArguments args, Action<int> value)
{
   service.AddAsync(args.Operand1, args.Operand2, value);
}

public void DoStuff(Action value)
{
   service.DoStuffAsync(value);
}

public class AddArguments
{
   public int Operand1 { get; set; }
   public int Operand2 { get; set; }
}
}

Os podeis descargar de aquí el ejemplo completo. http://www.luisguerrero.net/downloads/servicehelper.zip

Saludos. Luis.


Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Tags: , ,
Categorias: .Net | Silverlight
Acciones: E-mail | Permalink | Comments (0) | Comment RSSRSS comment feed

TPL – Cancelación de Task

September 13, 2009 18:29 por Luis Guerrero

¡Hola a todos! Seguimos con los post sobre Task Parallel Library de .NET Framework 4, en este artículo vamos a ver cuál es el soporte de cancelación de Task que tenemos en TPL.

Como hemos comentado en el artículo anterior las Task son la unidad mínima de ejecución de TPL, incluso PLINQ (Parallel LINQ) utiliza Task internamente para sus operaciones. También hemos visto como las Task tienen alguna similitud con los Worker Threads del ThreadPool de .NET.

Cuando nosotros en código lanzamos un Worker Thread con el método ThreadPool.QueueWorkItem, estamos encolando un trabajo para que se ejecute en otro Thread diferente, pero no tenemos control en como este Worker thread se ejecuta, de hecho no podemos saber cuándo se ha completado, no podemos cancelarlo y no podemos devolver un resultado de esa ejecución asíncrona.

Esto ahora con las Task cambia, ahora a través de la propiedad Status podemos saber en qué estado está nuestra Task, la propiedad Result nos dará el resultado de la ejecución de la Task. Pero, ¿Qué hay de la cancelación?.

Cancelar un Task no es una tarea trivial, porque implica muchas decisiones y puede conllevar muchos errores. Si pensamos en cómo se cancela un Thread, el CLR lo que hace es inyectar un ThreadAbortException en la ejecución del thread haciendo que este se aborte, pero lo que no podemos controlar es en qué punto de la ejecución del thread se va a producir esto, y lo más peligroso de eso es que podemos estar dentro de un bloque de lock y la inyectarse la excepción puede pasar que nunca se salga de ese bloque de lock, haciendo que pueda pasar un deadlock en nuestro código. Este es solo un ejemplo de lo que puede pasar pero por supuesto puede ser mucho más complicado.

¿Cómo se puede cancelar una Tarea de manera segura?

Hay dos opciones posibles, si la tarea esta creada y se ha llamado al método Start y la Task está esperando para ejecutarse (Status=WaitToRun) y se cancela TPL automáticamente cancelará la tarea por nosotros (Status=Cancelled). Pero si la tarea está ejecutándose o esperando a un evento externo se realiza de otra manera.

Para poder ver esta segunda parte tenemos que comprender primero como se gestiona esto en código para entender cuál es el mecanismo que TPL nos proporciona para cancelar Task.

Task t = new Task((index) =>
{
    Thread.SpinWait(1000);
    if (!cancellationSource.IsCancellationRequested)
    {
        Thread.Sleep(r.Next(0, 1000));
    }
}, i, cancellationSource.Token);
list.Add(t);

Si nos fijamos en este ejemplo de código al crear una instancia de la clase Task ahora pasamos por parámetro un CancellationToken que nos va a permitir comunicarnos con el sistema de cancelación de TPL. Ese CancellationToken viene de una estructura llamada CancellationTokenSource que es realmente la que gestiona la comunicación de la cancelación de las Task.

Lo primero que hacemos es generar una lista con todas las Task que vamos a ejecutar, con eso lo que tenemos es instancias de la clase Task, pero todavía no se están ejecutando, es después cuando llamamos al método Start de todos los objetos Task a la vez.

Como el TaskScheduler predeterminado es el ThreadPool de .NET al principio en una carga de trabajo tan grande 10000 Task, el ThreadPool se tendrá que tunear a sí mismo para empezar a crear Workers Threads y aumentar dinámicamente la capacidad de ejecución por nosotros. Esta es una de las cosas más cómodas de TPL, porque nosotros no tememos que directamente estar pendiente de la heurística del ThreadPool para saber cuál es el número de Threads ideal para nuestro sistema.

Además de todo esto tenemos un método que nos permite cancelar todas las Task a la vez.

public void CancelTask()
{
    cancellationSource.Cancel();
}

Simplemente lo que hacemos es llamar a Cancel del objeto CancellationTokenRequest y este se comunicará con la infraestructura de TPL para empezar la cancelación. Como dijimos antes la cancelación de tareas que están es estado WaitingToRun es automática, pero las que se están ejecutando es más complicado.

Si nos fijamos en el ejemplo de arriba, en el cuerpo del método que se ejecutará por cada Task, hay una sentencia condicional if !cancellationSource.IsCancellationRequested en la que estamos comprobando que no se ha realizado una solicitud de cancelación de tareas. Esto es así porque es la única manera segura y organizada de cancelar un Task. Este ejemplo no es muy representativo para el soporte de cancelación, pero por ejemplo si en esta Task se estuvieran generando por pasos un informe que tarda mucho tiempo, generar el reporte, consulta a la base de datos, ect, podemos hacer comprobaciones en cada paso de que se ha solicitado una cancelación de la Task y así de manera segura cancelar la petición y liberar todos los recursos generados por ese reporte.

Para poder sacar todo el partido de la cancelación en nuestras Task, tenemos que trabajar activamente con TPL para que la experiencia de cancelación sea la más adecuada a cada situación, habrá pasos que no se puedan cancelar hasta un determinado punto, en cualquier caso TPL nunca abortará la ejecución de ese Task por nosotros, pues como he comentado antes esto puede conllevar muchos problemas.

image

Otro de los escenarios posibles cuando se trabaja con cancelación es que de alguna manera nos gustaría saber cuándo un CancellationToken se ha cancelado, para eso tenemos varias opciones, porque el propio CancellationToken tiene un método para que registremos un delegado de tipo Action que será llamado cuando se cancele el CancellationToken. También existe la opción de registrar un objeto de tipo ICancelableOperation, que es la interfaz que implementan el tipo CancellationTokenSource para poder enlazar así objetos de cancelación y crear dependencias entre ellos.

Aquí os podéis descargar el código de ejemplo de la aplicación de WPF.

http://www.luisguerrero.net/downloads/TaskCancel.zip

Saludos. Luis.


Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Task Parallel Library - Introducción

September 6, 2009 17:31 por Luis Guerrero

Una de las nuevas novedades que .NET Framework 4.0 incluye es el la Task Parallel Library una serie de APIS nuevas para la programación multihilo. La idea principal de esta librería, que viene incluida en el propio framework, es que cuando tengamos que añadir paralelismo y concurrencia a nuestras aplicaciones sea de lo más sencillo.

Actualmente los procesadores ya no incrementan la velocidad en Gigahercios sino que lo que hacen es replicar el hardware haciendo que nos encontremos dentro del mismo encapsulado FPGA dos procesadores exactamente iguales con sus caches de segundo y primer nivel. Esto cambia la manera de desarrollar software porque ya no nos encontramos con procesadores más rápidos sino con procesadores con más cores, 2,4,6,8 ect.

Con este nuevo escenario tenemos los desarrolladores tenemos que empezar a paralelizar nuestro software para explotar toda la potencia de los procesadores.

Una de las cosas buenas de la TPL, es que si la maquina donde se va a ejecutar tiene 2, 4 o 8 procesadores, la TPL es capaz de escalar sin necesidad de recompilar ni configurar, es decir es capaz de usar todos los cores disponibles.

Eso quiere decir que cuanto más cores utilicemos más velocidad ganaremos, aunque esto no es siempre así, porque no todas las tareas son sensibles de ser paralelizadas. Además hay que tener en cuenta que usar la TPL añade complejidad en la ejecución de la aplicación y esto en algunas ocasiones puede hacer que se degrade el rendimiento y no lo aumente. Hablaremos de eso más adelante.

Aquí tenemos la primera toma de contacto con la TPL:

Parallel.For(startIndex, endIndex, (currentIndex) => DoSomeWork(currentIndex));

Parallel.For nos permite ejecutar de manera concurrente el cuerpo de un bucle for, haciendo que la ejecución se propague por todos los cores disponibles en el sistema. Así de sencillo.

Pero como hemos comentado antes no esto a veces aumenta el rendimiento y en otros lo degrada. Veamos porque.

Cuando realizamos un bucle normal todo el código se ejecuta de manera secuencial, es decir una instrucción detrás de otra, pero cuando estamos realizando una paralelizacion de nuestro código tenemos varios threads que están ejecutando código en el mismo instante de tiempo. Si nos ponemos a pensar cómo se podría implementar a mano un Parallel.For, lo primero que tendríamos que hacer es realizar una partición de las iteraciones para dependiendo de los procesadores que tengamos repartir el trabajo.

Si tenemos por ejemplo que recorrer una lista de 50000 elementos y tenemos 2 procesadores, podemos partir la lista de dos sublistas de 25000 y generar dos threas que se encarguen de recorrer esos elementos. Ponemos los threads a ejecutar y tenemos que sincronizar cuando los dos threads terminan.

Básicamente esto es de lo que se encarga el Parallel.For de hacer por nosotros, de una manera cómoda y elegante.

Ahora bien, ¿Cuándo no es recomendable realizar un Parallel.For?, hay una regla que funciona en la mayoría de los casos, cuando el tiempo de ejecución del cuerpo del for sea mayor o igual que el tiempo de creación de los threas y de la sincronización, también es lo mismo si tenemos colecciones pequeñas. ¿Qué quieres decir esto?, veamos un ejemplo:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;

namespace ParalleFor
{
    class Program
    {
        static void Main(string[] args)
        {
            new Program();
        }

        private List<int> GetRandomList()
        {
            List<int> list = new List<int>();
            for (int i = 0; i < 90000000; i++)
            {
                list.Add(i);
            }
            return list;
        }

        public Program()
        {
            List<int> list = GetRandomList();

            Stopwatch st = new Stopwatch();

            // sum all
            decimal value = 2;
            st.Start();
            for (int i = 0; i < list.Count; i++)
            {
                decimal final = value * list[i];
            }
            st.Stop();
            Console.WriteLine(st.Elapsed);
            st.Reset();

            st.Start();
            Parallel.For(0, list.Count, index =>
            {
                decimal final = value * list[(int)index];
            });
            st.Stop();
            Console.WriteLine(st.Elapsed);
        }
    }
}

Si nos fijamos la lista de con la que trabajamos es realmente grande, eso significa que si paralelizamos el bucle for ganaremos en rendimiento como muestra la salida de la ejecución. Pero si tenemos una lista pequeña no ganaremos tiempo sino que perderemos porque tenemos que sincronizar los n threas para esperar a que todos terminen, además del tiempo necesario para inicializarlos.

Otro de los grandes problemas de la paralelizacion es que necesitamos estructuras para sincronizar nuestro código y tener claro los conceptos de: locks, deadlocks, race condition (data and flow), etc.

En el siguiente artículo veremos el soporte para Task, las unidades básicas de ejecución dentro de TPL.

Aquí tenéis el código fuente del ejemplo.

http://www.luisguerrero.net/downloads/parallefor.zip

 

Saludos. Luis.


Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tool: Memory Pressure

June 23, 2009 21:40 por Luis Guerrero

Esta sencilla herramienta permite generar presión en la memoria del sistema a nuestro gusto.

clip_image002

La interfaz de usuario es muy sencilla, podemos seleccionar la cantidad de Megabytes que queremos reservar y cuál es el tamaño de los bloques que queremos usar. Hay que tener en cuenta que esta aplicación utiliza la reserva de memoria del heap de Windows, es decir llama a Marshal.AllocHGlobal que a su vez llama a LocalAlloc.

Una vez que tenemos la memoria reservada podemos liberarla para poder eliminar la presión.

Durante la reserva de memoria se llama a GC.AddMemoryPressure que notifica al recolector de basura la presión actual que se está haciendo en memoria nativa.

Os podeis instalar este software desde: http://www.luisguerrero.net/applications/MemoryPressure/

Tambien os podeis descargar el código fuente: http://www.luisguerrero.net/downloads/MemoryPressure.zip

Luis.


Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Constructores y Finalizadores

June 15, 2009 22:45 por Luis Guerrero

Dentro de la orientación a objetos nos encontramos dos tipos de métodos especiales, el constructor y el destructor. En .NET hay dos tipos de constructores de instancia (ctor) y de tipo (cctor) también llamado constructor estático.

Constructores

Cuando un tipo es accedido por primera vez, se ejecuta el constructor estático bajo un doble lock (Como implementar un Singleton concurrente) para asegurar que solamente se llama una vez, si además se está creando una instancia del objeto se llamará al constructor del objeto para inicializarlo. También se llama a todos los constructores base hasta que se llega a System.Object.

Hay que tener en cuenta que el constructor de un objeto no es más que un método que puede aceptar parámetros y que devuelve una referencia sí mismo a this. Como programadores no tenemos que manejar esta situación puesto que el propio compilador la realiza por nosotros.

Hay un caso excepcional en el que podemos crear un objeto pero no obtener una referencia a el nunca, si en el constructor se lanza una excepción. En este caso, el objeto se inicializa, se llama a los constructores base y se aborta el método en el punto en el que se lance la excepción, pero ya se ha reservado memoria para el objeto y nunca podremos acceder a esa referencia de ese objeto a medio construir.

public class Foo
{
    public DateTime DateTime { get; set; }
    public string file { get; set; }
    public Foo()
    {
        DateTime = DateTime.Now;
        string[] v = file.Split(',');
    }
}

Aquí tenemos un ejemplo de una clase que en el constructor lanzará una excepción de tipo NullReferenceException, realmente si se ha creado el objeto solo que si hacemos algo como:

Foo f = new Foo();

Nunca llegaremos a obtener la referencia del objeto hay creado y además tenemos que tener en cuenta que se tendrá que recolectar el objeto para liberar memoria en la siguiente recolección de basura.

Es por eso que hay que tener mucho cuidado con lo que se pone en el constructor porque podemos dejar a nuestro tipo inutilizado.

La situación se agrava si se lanza una excepción en el constructor de tipo, ya que no podremos acceder a ninguna variable estática ni crear instancias de este tipo.

Destructores o finalizadores

Los destructores (o finalizadores) son los métodos que se llamarán cuando el objeto ya no es usado y se reclama la memoria usada. En este caso es el CLR el que se encarga de llamar a este finalizador pues nosotros no tenemos ningún mecanismo para liberar un objeto.

Un finalizador se define de esta manera:

~Foo()
{
   Console.WriteLine("Adiossss !!");
}

Eso significa que cuando esa instancia del tipo Foo sea recolectada se ejecutará este método. Si miramos al IL generado por este programa realmente el destructor ha pasado a llamarse Finalize() y está marcado como virtual.

Además de definir un destructor de clase hay otro mecanismo para poder definir un destructor, implementando la interfaz IDisposable. Este es el único mecanismo que tiene un programador de .NET para poder definir un destructor de objeto y además poder llamarlo el mismo de codigo de usuario.

Para evitar que este método sea llamado otra vez por el CLR existe el método GC.SupressFinalize.

¿Cómo es llamado este método?

Cada vez que se realiza una recolección de basura el GC examina todos los objetos y si encuentra alguno que es finalizable, es decir, tiene destructor o implementa IDisposable, entonces ese objeto que está listo para ser recolectado pasa a la cola de finalización.

Cuando los objetos están en esa cola de finalización un Thread de alta prioridad se encarga de sacar un elemento de la cola, ejecutar su finalizador y marcarlo como listo para ser recolectado.

Consideraciones a tener en cuenta:

· Cuando el objeto está listo para ser finalizado se lleva a la cola de ReadyToFinalize y además todos los objeto de su grafo son marcados. Eso significa que si ese grafo de objeto normalmente moriría en esta recolección, ahora es promovida a la siguiente, simplemente porque su objeto raíz es finalizable.

· Las generaciones más viejas son recolectadas menos que las generaciones más jóvenes lo que indica que nuestro grafo de objetos puede permanecer vivo durante mucho tiempo hasta que es completamente finalizado y recolectado.

· Al utiliza un único thread, aunque está en prioridad alta, puede darse el caso de que el si tenemos una maquina con 32 procesadores y solamente tenemos una finalizado objetos la cola pueda crecer mucho.

· Hay que tener mucho cuidado con lo que se ejecuta en el finalizador porque si se produce una excepción el proceso entero se descarga. No estamos hablando de que se descargue el dominio de aplicación sino todo el proceso.

Garantías de finalización:

· Durante la ejecución normal de la aplicación y durante cada recolección de basura, se llamara a los finalizadores de los objetos que haya en la cola.

· Si el proceso termina sin la coordinación del CLR, llamando a TerminateProcess o ExitProcess, el CLR tendrá su primera noticia de que el proceso se está cerrando cuando se envíe el mensaje DLL_PROCESS_DETACH en el DllMain. Así que el CLR no tiene tiempo de hacer nada y lo único que puede pasar es que termine los finalizadores críticos (CriticalFinalizerObject).

· Si el proceso es cerrado con el pleno conocimiento del CLR, llamado a Exit o Enviroment.Exit, se finalizarán los objetos que están en la cola de finalización y los finalizadores críticos. Pero los posibles objetos que salgan de una recolección de basura listos para ser finalizados se quedarán sin ser finalizados.

· Si se descarga el dominio de aplicación entonces solamente se finalizarán los objetos que estuvieran en la cola de finalización y los finalizadores criticos.

El codigo del ejemplo: http://www.luisguerrero.net/downloads/ctor.zip

Saludos. Luis.


Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Rendimiento para el modelado de clases

June 2, 2009 01:22 por Luis Guerrero

Rendimiento. En muchos proyectos en los que trabajo una de las preocupaciones a la hora de hacer el proyecto es el rendimiento de la aplicación

Una de las tareas, por no decir la única, es trabajar con datos en una aplicación, modelamos constantemente clases que tiene estado y a su vez exponen una serie de métodos para que los podamos invocar. Hoy a lo que me voy a dedicar a explicar es justamente a ese modelado de datos, al estado de nuestras clases.

Dentro de .NET Framework tenemos varias maneras de definir el estado de una clase:

  • Field (Campo)
  • Property (Propiedad)
  • DependencyProperty (Propiedades de Dependencia)

Estas últimas están pensadas para la interacción de esas clases con Interfaz de Usuario (UI).

Las propiedades son una normalización de los métodos de acceso (set) o de obtención (get) que normalmente se hacía para obtener el acceso a los campos o modificarlos. Realmente las variables se guardan en los campos (field) solo que las propiedades permiten normalizar el acceso y encapsular su funcionalidad de acceso. Son implementadas como métodos dentro del ensamblado.

Lo que vamos a ver en este post es cual la velocidad de acceso a estas propiedades desde .net para saber cuál es la mejor opción si estamos trabajando con el estado de las propiedades y campos. Cualquiera me podría decir que según la lista anterior está claro que siempre la mejor opción (desde el punto de vista del rendimiento) son los campos (Field) puesto que accederos directamente y sin intermediarios, pero además de eso cuando los proyectos empiezan a complicarse puede pensar en usar la reflexión para acceder a los datos, porque se quiere automatizar el acceso a las propiedades de las clases.

Así que lo que vamos a hacer es tener dos tipos de clases una con campos y propiedades, (Field y Property), otra con DependencyProperty y vamos a medir el tiempo que tardamos en acceder a esas variables. Una de las desventajas de usar DP es que para poder usar su funcionalidad tenemos que heredar de la clase DependencyObject, que es la clase que implementa la funcionalidad de DP. Además como he comentado anteriormente las DP únicamente se usar para definir las propiedades de los controles de interfaz de usuario y de los objetos de negocio que interactúan con la UI.

Lo que vamos a hacer son una serie de pruebas con una clase intermedia para medir el tiempo y que se encargue de ejecutar el bloque de código n veces y de medir el tiempo que tarda en hacerlo.

Para la medida del tiempo no vamos a usar DateTime.Now antes de empezar y después de ejecutar porque la medida de tiempo de DateTime.Now no es de alta resolución y no vamos a tener la precisión que necesitamos para medir diferencias de tiempo. En vez de DateTime.Now vamos a usar Stopwatch, clase que está en System.Diagnosis que nos permite hacer medidas de tiempo en alta resolución, el uso de esta clase es bastante sencilla así que el lector la podrá descubrir por el mismo.

Vamos al lio, tenemos está clase normal de C#

public class NormalType
{
    internal int number;

    public int Number
    {
        get { return number; }
        set { number = value; }
    }

    internal string _string;

    public string String
    {
        get { return _string; }
        set { _string = value; }
    }
    internal Item item;

    public Item Item
    {
        get { return item; }
        set { item = value; }
    }
}

Como podéis ver es una clase con tres propiedades un entero, un string y un tipo referencia complejo (Item), todas las propiedades tienen un campo que respalda el valor, así que realmente las propiedades simplemente exponen los valores de los campos sin más.

public class DependencyPropertyType : DependencyObject
{
    public int Number
    {
        get { return (int)GetValue(NumberProperty); }
        set { SetValue(NumberProperty, value); }
    }

    public static readonly DependencyProperty NumberProperty =
        DependencyProperty.Register("Number", typeof(int), typeof(DependencyPropertyType), new PropertyMetadata(0));

    public string String
    {
        get { return (string)GetValue(StringProperty); }
        set { SetValue(StringProperty, value); }
    }

    public static readonly DependencyProperty StringProperty =
        DependencyProperty.Register("String", typeof(string), typeof(DependencyPropertyType), new PropertyMetadata(null));

    public Item Item
    {
        get { return (Item)GetValue(ItemProperty); }
        set { SetValue(ItemProperty, value); }
    }

    public static readonly DependencyProperty ItemProperty =
        DependencyProperty.Register("Item", typeof(Item), typeof(DependencyPropertyType), new PropertyMetadata(null));

}

Esta otra clase es la clase que implementa las DependencyProperty, como veis ahora por cada propiedad hay una definición de una clase de tipo DependencyProperty que es estático y de solo lectura (static readonly) y además no es el tipo que definimos las propiedades. Eso que significa que los valores de esta clase son compartidos, desde luego no porque las propiedades son de instancia no estáticas, solo que ahora utilizamos dos métodos helpers que nos ayudan a acceder y establecer los valores de las propiedades (GetValue,SetValue). Esos dos métodos están definidos en la clase base DependencyObject. Realmente nosotros no tenemos información de donde se están guardando esas variables dentro de nuestra clase no hay ningún campo (Field) ni lista ni nada que nos indique donde se están almacenando esos valores, y si miramos en la clase base nos pasa lo mismo. En otro post desvelare toda la potencia y misterios de las DP ahora simplemente lo usaremos sin más para esta prueba de rendimiento.

Ahora lo que vamos a hacer son las pruebas en sí de acceso a estos valores.

image 

Sin reflexion

Con Reflexion

Getter Field

92,176

4483,588

Getter Property

142,813

3169,277

Getter Dependency Property

755,433

1619,854

Setter Field

0,607

3716,121

Setter Property

0,584

2600,364

Setter Dependency Property

129,623

973,799

La prueba está dividida en dos grandes grupos Setters y Getters (establecer y obtener).

Para los Setters (establecer) los campos tiene el mejor rendimiento cuando se trabaja con ellos, pero son los que tiene el peor rendimiento cuando se trata de acceder a través de reflexión, las propiedades es el mismo caso solo que se mejora un poco el tiempo de acceso desde reflexión y para las Dependency Property están muy equilibradas en cuanto a tiempo de acceso con y sin reflexión.

En el caso de Getters (obtener) la historia se repite, los campos y propiedades son increíblemente rápidos, pero cuando se trabaja con ellos a través de reflexión se penaliza, las Dependency Property en relación con el Setter son más rápidas con y sin reflexión.

El caso especial de las Dependency Proeprty.

¿Por qué las Dependency Properties tiene mejores tiempos?, podríamos decir que la prueba esta adulterada, porque realmente siempre se utiliza los métodos GetValue y SetValue que DependencyObject nos proporciona para acceder a esos valores y ahí es donde está la sobrecarga pero hay que tener en cuenta que la reflexión usada en esta prueba es solo para acceder a las instancias de las DependencyProperty que son necesarias en las llamadas de GetValue y SetValue.

Por si sentís curiosidad los valores de las instancias de clases que heredan de DependencyObject se guardan en un campo interno de la clase DependencyObject llamado _effectiveValues de tipo EffectiveValueEntry,

private EffectiveValueEntry[] _effectiveValues;

EffectiveValueEntry es la clase que manera el valor de una Dependency Property, cada una de estos EffectiveValueEntry tiene asociado un índice dentro de todos los registros de Dependency Proeprty al cual nosotros podemos acceder desde DependencyProperty.GlobalIndex (int) pero no podemos usar de ninguna manera, no sé porque Microsoft ha permitido acceder a esta propiedad si no tiene utilizad.

La solución de Visual Studio para que hagáis vuestras pruebas.

http://www.luisguerrero.net/downloads/DPPerformance.zip

Luis Guerrero.


Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Como implementar un Singleton concurrente

May 23, 2009 12:33 por Luis Guerrero

Bajo este título se encuentra una de los clásicos problemas de concurrencia que seguramente más de uno se haya enfrentado en su vida de programador. En este artículo repasaremos las posibles implementaciones correctas e incorrectas de este patrón de manera concurrente.

Implementación sin concurrencia.

   1: public class Singleton<T> where T : new()
   2: {
   3:     private static T instance = new T();
   4:     public static T Instance
   5:     {
   6:         get
   7:         {
   8:             return instance;
   9:         }
  10:     }
  11: }
  12: public class SingletonV2<T> where T : new()
  13: {
  14:     private static T instance;
  15:     public static T Instance
  16:     {
  17:         get
  18:         {
  19:             if (instance == null)
  20:             {
  21:                 instance = new T();
  22:             }
  23:             return instance;
  24:         }
  25:     }
  26: }
  27: public class CacheManager
  28: {
  29:     private static CacheManager instance = new CacheManager();
  30:     public static CacheManager Instance
  31:     {
  32:         get
  33:         {
  34:             return instance;
  35:         }
  36:     }
  37:     private CacheManager()
  38:     {
  39:  
  40:     }
  41: }

Como se puede ver esta es la implementación para una clase cualquiera y de una manera genérica, en la que el único requisito que pedimos es que sea una referencia y se pueda construir una instancia.

El problema de estas dos implementaciones es que cuando se construye el tipo se inicializa el valor del Singleton, lo que puede resultar en una degradación del rendimiento y solamente se desea implementar cuando se vaya a acceder al valor de la instancia. Para solucionar ese problema se puede implementar un Singleton perezoso que solamente cuando se accede la primera vez se inicializa.

   1: public class CacheManagerV2
   2: {
   3:     private static CacheManagerV2 instance;
   4:     public static CacheManagerV2 Instance
   5:     {
   6:         get
   7:         {
   8:             if (instance == null)
   9:             {
  10:                 instance = new CacheManagerV2();
  11:             }
  12:             return instance;
  13:         }
  14:     }
  15:     private CacheManagerV2()
  16:     {
  17:  
  18:     }
  19: }

Pero llegado a este punto nos encontramos con un problema muy importante, que pasa si dos Threads a la vez intenta acceder al valor de la instancia de cualquiera de nuestros Singletones, el resultado puede ser catastrófico, porque se puede iniciar más de una instancia de la clase o cada uno de los Threas se puede llevar una referencia distinta del singleton haciendo que trabajen con instancias diferentes.

¿Cómo se puede solucionar este problema?

Hay varias maneras de solucionarlo, la primera de todas sería usar un bloqueo para sincronizar el acceso a este recurso. Vamos a ver una serie de ejemplos y porque estos ejemplos están bien o mal implementados.

Utilizando bloqueos

   1: public class BadCacheManager
   2: {
   3:   private static BadCacheManager instance;
   4:   private static object syncRoot = new object();
   5:   public static BadCacheManager Instance
   6:   {
   7:       get
   8:       {
   9:           S0
  10:           if (instance == null)
  11:           {
  12:               S1
  13:               lock (syncRoot)
  14:               {
  15:                   instance = new BadCacheManager();
  16:               }
  17:           }
  18:           return instance;
  19:       }
  20:   }
  21:   private BadCacheManager()
  22:   {
  23:  
  24:   }
  25: }

BadCacheManager: Mal

Esta implementacion no funcionaría porque puede darse la casualizad de que durante la primera comprobación (S0) y justo antes de que se instancie la clase (S1) puede haber una instrucción y puede darse la casualidad de que se interrumpa el thread (t0) justo en ese instante, lo que otro thread (t1) evaluaria S0 (true, es nulo) adquiriría el bloqueo pero esperaría (t1) porque el otro thread (t0) lo tiene asignado, así que t0 se despertaría crearia el objeto, después t1 haría lo mismo dando como resultado dos instancias. Además de todo esto no se sincroniza el almacenamiento de la variable instance con un memory barrier (fence) marcando la variable como volatile o usando Thread.MemoryBarrier().

   1: public class DoubleLockVolatileCacheManager
   2: {
   3:     private static volatile DoubleLockVolatileCacheManager instance;
   4:     private static object syncRoot = new object();
   5:     public static DoubleLockVolatileCacheManager Instance
   6:     {
   7:         get
   8:         {
   9:             if (instance == null)
  10:             {
  11:                 lock (syncRoot)
  12:                 {
  13:                     if (instance == null)
  14:                     {
  15:                         instance = new DoubleLockVolatileCacheManager();
  16:                     }
  17:                 }
  18:             }
  19:             return instance;
  20:         }
  21:     }
  22:     private DoubleLockVolatileCacheManager()
  23:     {
  24:  
  25:     }
  26: }

DoubleLockVolatileCacheManager: Bien *

Esta implementación esta bien pero a medias, en la implementacion de .NET el CLR se asegura que independientemente del tipo de reordenacion del procesador, del modelo de memoria y de la atomicidad de las lecturas y escrituras siempre funciona, de hecho es lo que .net utiliza internamente para asegurarse que el constructor estatico (cctor) de un tipo solamente se ejecute una vez. Pero el modelo de memoria de .NET permite reordenaciones de lectura/escritura de variables no volatiles, así que habría que haber marcado la instancia como volatie o insertar un Thread.MemoryBarrier, aquí tenemos la implementacion correcta.

   1: public class DoubleLockCacheManager
   2: {
   3:    private static DoubleLockCacheManager instance;
   4:    private static object syncRoot = new object();
   5:    public static DoubleLockCacheManager Instance
   6:    {
   7:        get
   8:        {
   9:            if (instance == null)
  10:            {
  11:                lock (syncRoot)
  12:                {
  13:                    if (instance == null)
  14:                    {
  15:                        DoubleLockCacheManager tmp = new DoubleLockCacheManager();
  16:                        Thread.MemoryBarrier();
  17:                        instance = tmp;
  18:                    }
  19:                }
  20:            }
  21:            return instance;
  22:        }
  23:    }
  24:    private DoubleLockCacheManager()
  25:    {
  26:  
  27:    }
  28: }
   1: public class BadLazy<T>
   2: {
   3:   private T internalValue;
   4:   private bool isInitialized;
   5:   private object syncRoot = new object();
   6:   private Func<T> factory;
   7:  
   8:  
   9:   public BadLazy(Func<T> factory)
  10:   {
  11:       this.factory = factory;
  12:   }
  13:  
  14:   public T Value
  15:   {
  16:       get
  17:       {
  18:           lock (syncRoot)
  19:           {
  20:               if (!isInitialized)
  21:               {
  22:                   internalValue = factory();
  23:                   isInitialized = true;
  24:               }
  25:           }
  26:           return internalValue;
  27:       }
  28:   }
  29: }

Todos los ejemplos que hemos utilizado aquí utilizan lock (aka Monitor.Enter) para implementar un sistema de bloqueo en los recursos compartidos del Singleton, pero lo ideal para casi todos los casos es no utilizar bloqueos.

¿Cómo se puede implementar un algoritmo libre de bloqueos?, la respuesta está en la granularidad de la concurrencia, gruesa o fina. Nosotros queremos granularidad fina para hacer que los Threads estén el menos tiempo en un bloqueo haciendo así que todo el sistema responda mucho mejor. Una granularidad fina es mucho más complicada de implementar pero tiene un mejor rendimiento y respuesta del sistema porque hay menos contención.

Ahora vamos a ver como sería el Singleton perezoso sin bloqueos.

   1: public class Lazy<T> where T : class, new()
   2: {
   3:    private T value;
   4:    public T Value
   5:    {
   6:        get
   7:        {
   8:            if (value == null)
   9:            {
  10:                Interlocked.CompareExchange(ref value, new T(), null);
  11:            }
  12:            return value;
  13:        }
  14:    }
  15: }

Como se puede observar se ha simplificado mucho el código y ahora lo único que tenemos es un Interlocked.CompareExchange, en el que se compara el valor de primer argumento con el del último argumento y si son iguales entonces se establece en el primer argumento el valor de segundo parámetro, así que si nuestra instancia es nula entonces se crea la instancia y se establece. Lo interesante de esta forma de implementarlo es que no tenemos que preocuparnos por el modelo de memoria de .net por la reordenación de instrucciones ni por nada, ya que el Interlocked.CompareExchange es atómica a nivel de hardware, es decir nuestro procesador nos asegura que sea instrucción CMPXCHG es atómica.

Como se puede observar la granularidad de este algoritmo es muy fina porque solamente se bloquea en el momento justo de establecer la variable.

Un ejemplo de una granularidad fina en el uso de los bloqueos la podemos observar en la implementación de este diccionario concurrente.

   1: public class ConcurrentDictionary<TKey, TValue> : IDictionary<TKey, TValue>, ICollection<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>
   2: {
   3:     private ReaderWriterLock rw = new ReaderWriterLock();
   4:     private Dictionary<TKey, TValue> dic = new Dictionary<TKey, TValue>();
   5:     private int timeout = -1;
   6:     public void Add(TKey key, TValue value)
   7:     {
   8:         rw.AcquireWriterLock(timeout);
   9:         try
  10:         {
  11:             if (!dic.ContainsKey(key))
  12:             {
  13:                 dic.Add(key, value);
  14:             }
  15:         }
  16:         finally
  17:         {
  18:             rw.ReleaseWriterLock();
  19:         }
  20:     }
  21:  
  22:     public bool ContainsKey(TKey key)
  23:     {
  24:         bool res = false;
  25:         rw.AcquireReaderLock(timeout);
  26:         try
  27:         {
  28:             res = dic.ContainsKey(key);
  29:         }
  30:         finally
  31:         {
  32:             rw.ReleaseReaderLock();
  33:         }
  34:         return res;
  35:     }
  36:  
  37:     public ICollection<TKey> Keys
  38:     {
  39:         get
  40:         {
  41:             ICollection<TKey> res = null;
  42:             rw.AcquireReaderLock(timeout);
  43:             try
  44:             {
  45:                 Dictionary<TKey, TValue> tmp = new Dictionary<TKey, TValue>(dic);
  46:                 res = tmp.Keys;
  47:             }
  48:             finally
  49:             {
  50:                 rw.ReleaseReaderLock();
  51:             }
  52:             return res;
  53:         }
  54:     }
  55:  
  56:     public bool Remove(TKey key)
  57:     {
  58:         bool res = false;
  59:         rw.AcquireWriterLock(timeout);
  60:         try
  61:         {
  62:             res = dic.Remove(key);
  63:         }
  64:         finally
  65:         {
  66:             rw.ReleaseWriterLock();
  67:         }
  68:         return res;
  69:     }
  70:  
  71:     public bool TryGetValue(TKey key, out TValue value)
  72:     {
  73:         bool res = false;
  74:         rw.AcquireWriterLock(timeout);
  75:         try
  76:         {
  77:             res = dic.TryGetValue(key, out value);
  78:         }
  79:         finally
  80:         {
  81:             rw.ReleaseWriterLock();
  82:         }
  83:         return res;
  84:     }
  85:  
  86:     public ICollection<TValue> Values
  87:     {
  88:         get
  89:         {
  90:             ICollection<TValue> res = null;
  91:             rw.AcquireReaderLock(timeout);
  92:             try
  93:             {
  94:                 Dictionary<TKey, TValue> tmp = new Dictionary<TKey, TValue>(dic);
  95:                 res = tmp.Values;
  96:             }
  97:             finally
  98:             {
  99:                 rw.ReleaseReaderLock();
 100:             }
 101:             return res;
 102:         }
 103:     }
 104:  
 105:     public TValue this[TKey key]
 106:     {
 107:         get
 108:         {
 109:             TValue res = default(TValue);
 110:             rw.AcquireReaderLock(timeout);
 111:             try
 112:             {
 113:                 if (dic.ContainsKey(key))
 114:                 {
 115:                     res = dic[key];
 116:                 }
 117:             }
 118:             finally
 119:             {
 120:                 rw.ReleaseWriterLock();
 121:             }
 122:             return res;
 123:         }
 124:         set
 125:         {
 126:             if (ContainsKey(key))
 127:             {
 128:                 rw.AcquireWriterLock(timeout);
 129:                 try
 130:                 {
 131:                     dic[key] = value;
 132:                 }
 133:                 finally
 134:                 {
 135:                     rw.ReleaseWriterLock();
 136:                 }
 137:             }
 138:         }
 139:     }
 140:  
 141:  
 142:  
 143:     public void Add(KeyValuePair<TKey, TValue> item)
 144:     {
 145:         Add(item.Key, item.Value);
 146:     }
 147:  
 148:     public void Clear()
 149:     {
 150:         rw.AcquireWriterLock(timeout);
 151:         try
 152:         {
 153:             dic.Clear();
 154:         }
 155:         finally
 156:         {
 157:             rw.ReleaseWriterLock();
 158:         }
 159:     }
 160:  
 161:     public bool Contains(KeyValuePair<TKey, TValue> item)
 162:     {
 163:         return ContainsKey(item.Key);
 164:     }
 165:  
 166:     public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
 167:     {
 168:         throw new NotImplementedException();
 169:     }
 170:  
 171:     public int Count
 172:     {
 173:         get
 174:         {
 175:             int count = -1;
 176:             rw.AcquireReaderLock(timeout);
 177:             try
 178:             {
 179:                 count = dic.Count;
 180:             }
 181:             finally
 182:             {
 183:                 rw.ReleaseReaderLock();
 184:             }
 185:             return count;
 186:         }
 187:     }
 188:  
 189:     public bool IsReadOnly
 190:     {
 191:         get { return false; }
 192:     }
 193:  
 194:     public bool Remove(KeyValuePair<TKey, TValue> item)
 195:     {
 196:         bool res = false;
 197:         if (ContainsKey(item.Key))
 198:         {
 199:             Remove(item.Key);
 200:         }
 201:         return res;
 202:     }
 203:  
 204:  
 205:  
 206:     public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
 207:     {
 208:         IEnumerator<KeyValuePair<TKey, TValue>> res = null;
 209:         rw.AcquireReaderLock(timeout);
 210:         try
 211:         {
 212:             Dictionary<TKey, TValue> tmp = new Dictionary<TKey, TValue>(dic);
 213:             res = tmp.GetEnumerator();
 214:         }
 215:         finally
 216:         {
 217:             rw.ReleaseReaderLock();
 218:         }
 219:         return res;
 220:     }
 221:  
 222:  
 223:  
 224:     IEnumerator IEnumerable.GetEnumerator()
 225:     {
 226:         return null;
 227:     }
 228:  
 229:  
 230:  
 231:     IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
 232:     {
 233:         return null;
 234:     }
 235:  
 236:  
 237: }

En el que únicamente cuando se realizan operaciones en el diccionario se intenta bloquear lo menos posible además de que no se utiliza lock (aka Monitor.Enter) sino ReaderWriterLock que permite tener varios lectores y un solo escritor concurrentemente. Se podría haber utilizado ReaderWriterLockSlim que mejora sensiblemente el rendimiento pero esta implementación era para .NET 2.0 y ReaderWriterLockSlim solo funciona con .NET 3.5 además de que en Windows Vista se ha mejorado la implantación nativa.

Os podeis descargar el codigo de ejemplo de aquí

http://www.luisguerrero.net/downloads/Singleton.zip

Espero que os sirva de ayuda.

Saludos. Luis.


Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Taskbar de Windows 7 desde WPF

April 18, 2009 17:49 por Luis Guerrero

Ahora que se acerca el lanzamiento de Windows 7 tenemos que intentar que nuestras aplicaciones se integren de la mejor manera con Windows 7, y esto pasa por hacer que nuestras aplicaciones se lleven bien con la nueva Taskbar.

Como comentaba en mi artículo anterior http://www.luisguerrero.net/post/2009/04/05/Windows-7-Taskbar.aspx se pueden trabajar con varias características de la Taskbar, nosotros vamos a ver cómo crear una tarea personalizada y como añadir un pequeño botón a la pre visualización de la ventana como la del Windows Media Player. Todo esto lo vamos a hacer para nuestra aplicación de WPF, así que como programadores de .net no tenemos una API directamente para hacer esto así que tenemos que hacer P/Invoke desde .NET. Pero estamos de suerte porque Microsoft ha creado varias librerías para que los programadores de .NET usemos las nuevas características del SO directamente desde nuestro lenguaje favorito de .NET.

Para ello tenemos que descargar dos librerías

Nosotros vamos a trabajar con la librería de la Taskbar, pero está referencia al proyecto de Vista Bridge Library. Dentro del proyecto de la Taskbar encontramos una clase llamada Windows7Taskbar que nos va a permitir hacer una serie de cosas básicas con la taskbar como:

  • Habilitar o deshabilitar la pre visualización de miniatura de nuestra aplicación
    • public static void EnableCustomWindowPreview(IntPtr hwnd)
    • public static void DisableCustomWindowPreview(IntPtr hwnd)
  • ·Establecer el appId de nuestra aplicación para que así se agrupen las ventanas en la Taskbar:
    • public static void SetCurrentProcessAppId(string appId)
  • Establecer el appId para una de las ventanas de nuestra aplicación
    • public static void SetWindowAppId(IntPtr hwnd, string appId)

También vamos a poder trabajar con los JumpList y con los ThumbButton.

JumpList

Para trabajar con los JumpList tenemos una clase dentro del proyecto de Windows 7 Taskbar llamada JumpListManager, aquí podemos ver qué métodos, propiedades y eventos tiene.

Para crear una instancia de esta clase tenemos dos constructores en el que se nos pide o el HANDLE de la ventana o el AppId de la ventana.

image

Obtener el Handle de una ventana en Windows Forms es muy sencillo solo tenemos que acceder a la propiedad Handle de la clase Form y ya lo tenemos, pero ¿cómo se accede al manejador de una ventana de WPF?, si intentamos buscar esa propiedad en la clase System.Windows.Window de WPF observamos que no tenemos esa propiedad, es más, que ningún control de WPF tiene una propiedad Handle.

Esto es así porque como ya sabemos WPF es una tecnología nueva en la que no se utiliza nada de lo anterior para trabajar, y de hecho no es como Windows Forms que es un wrapper muy grande de la API de Win32, aquí en WPF se ha empezado desde cero. Pues bien tiene que haber alguna manera de por lo menos, obtener el Handle de la ventana porque de lo que estamos seguros es de que por mucho que el contenido de una ventana sea WPF o WF al final se llamada a la función CreateWindowEx que en alguno de los casos nos devolverá el Handle de la ventana.

Para obtener el Handle de la ventana en WPF tenemos que ir hasta el namespace System.Windows.Interop para encontrarnos con la clase WindowInteropHelper que una vez creada una instancia, pasándole por argumento la ventana (System.Windows.Window) podemos obtener el Handler (IntPtr) de la ventana y incluso su propietario Owner (o establecerlo). Una vez superado esta pequeña traba podemos continuar con nuestra integración de la Taskbar.

El siguiente problema que se nos plantea es cuando es el mejor momento para crear la instancia de esta clase, porque si lo hacemos antes de que el Handle de la ventana se haya construido, la llamada a la función fallará. Tenemos que elegir el mejor momento para hacerlo, ese momento puede ser cuando se lanza el evento Loaded de la ventana porque es cuando ya se ha construido el árbol visual, se ha hecho measure, arrange y se va a mostrar la ventana al usuario. Pero también el mejor momento puede ser cuando la Taskbar nos avista de que se va a proceder a colocar el icono de la aplicación en la propia Taskbar, este es el momento justo para hacerlo.

Ese momento del que estoy hablando es cuando la aplicación recibe el mensaje “TaskbarButtonCreated” que previamente ha sido registrado llamado a RegisterWindowMessage(L” TaskbarButtonCreated”). Teniendo en cuenta que ya hemos llamado a esa función como podemos escuchar los mensajes de la API de Win32 desde una aplicación WPF. En Windows Forms es muy sencillo porque tenemos que sobrescribir la función virtual WndProc en la que se nos pasa por referencia el Mensaje que se está procesado en ese momento, pero de la misma manera si vamos a WPF vemos que no tenemos esa función disponible para sobrescribir.

La cosa es un poco más complicada porque si alguno recuerda de otros post anteriores, en WPF tenemos un objeto llamado Dispatcher (System.Windows.Threading) que se encarga de procesar los mensajes de la API de Win32, pero de alguna manera queremos que después de que este Dispatcher procese los mensajes lo hagamos nosotros, básicamente lo que queremos hacer en un WindowHook, pero un poco más elegante. No tenemos que llamar a SetWindowHookEx sino que tenemos que explorar de nuevo System.Windows.Interop para encontrar la clave.

La clase que estamos buscando ahora es: HwndSource, que según la documentación presenta el contenido de WPF en una ventana de Win32, que es justamente lo que estamos buscando, pero el caso es que esta clase ya deberá de esta creada porque ya tenemos nuestra ventana creada, así que tenemos que obtener el HwndSource para nuestra ventana actual.

h = new WindowInteropHelper(Application.Current.MainWindow);

HwndSource source = HwndSource.FromHwnd(h.Handle);

Esta clase tiene un método llamado AddHook que acepta por parámetro un delegado que será la función que se llamará cada vez que haya un mensaje disponible para procesarse. Esta es la signatura de la función a ejecutar:

image

public IntPtr HwndSourceHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)

Lo último que nos queda es filtrar todos los mensajes que nos van a llegar por el mensaje que nos enviara la Taskbar diciendo que ya está lista. Podemos ver el código completo de todo esto aquí.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Windows7.DesktopIntegration;
using System.Windows;
using System.Windows.Interop;
using System.Diagnostics;
using System.Drawing;


namespace MainClient
{
    public class Taskbar
    {
        private static Taskbar instance = new Taskbar();
        public static Taskbar Instance
        {
            get
            {
                return instance;
            }
        }
        private IntPtr idWindow;
        private JumpListManager manager;
        private ThumbButtonManager thumbButtonManager;
        private WindowInteropHelper h;
        public Taskbar()
        {
            HwndSourceHook hook = new HwndSourceHook(HwndSourceHook);
            h = new WindowInteropHelper(Application.Current.MainWindow);
            HwndSource source = HwndSource.FromHwnd(h.Handle);
            source.AddHook(hook);
        }

        public IntPtr HwndSourceHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            if (msg == Windows7Taskbar.TaskbarButtonCreatedMessage)
            {
                manager = new JumpListManager(h.Handle);
                manager.UserRemovedItems += new EventHandler<UserRemovedItemsEventArgs>(OnUserRemoveItems);
                manager.Refresh();
                if (manager.CustomDestinationCategories.Count() == 0)
                {
                    // create list
                    manager.AddUserTask(new ShellLink()
                    {
                        Title = "Enviar mensaje",
                        Path = Process.GetCurrentProcess().MainModule.FileName,
                        Arguments = " /send"
                    });
                }

                manager.Refresh();

                thumbButtonManager = new ThumbButtonManager(h.Handle);
                ThumbButton button = thumbButtonManager.CreateThumbButton(101, SystemIcons.Information, "No molestar!!");
                button.Clicked += new EventHandler(OnChangeNotificationAlerts);
                thumbButtonManager.AddThumbButtons(button);
            }
            if (thumbButtonManager != null)
            {
                System.Windows.Forms.Message m = new System.Windows.Forms.Message();
                m.HWnd = hwnd;
                m.LParam = lParam;
                m.Msg = msg;
                m.WParam = wParam;
                thumbButtonManager.DispatchMessage(ref m);
            }
            return IntPtr.Zero;
        }

        void OnChangeNotificationAlerts(object sender, EventArgs e)
        {
            Services.Instace.IsNotificationEnabled = !Services.Instace.IsNotificationEnabled;
            if (!Services.Instace.IsNotificationEnabled)
            {
                Windows7Taskbar.SetTaskbarOverlayIcon(h.Handle, SystemIcons.Error, "Sin notificaciones");
            }
            else
            {
                Windows7Taskbar.SetTaskbarOverlayIcon(h.Handle, null, null);
            }
        }


        void OnUserRemoveItems(object sender, UserRemovedItemsEventArgs e)
        {

        }
    }
}

Una vez que ya tenemos la instancia del JumpListManager ya podemos agregar tareas a la Taskbar y asociarles una acción. En el código de arriba podemos ver que se añade una acción que es enviar mensaje que, básicamente, ejecuta la aplicación actual pero con el parámetro /send.

Otra posibilidad que hay con la Taskbar es poder insertar botones dentro de la previsualización de la ventana, como hace el Windows Media Player. A través de la clase ThumbButtonManager podemos agregar botones a la Taskbar y realizar una acción cuando se haga clic. En el ejemplo de arriba podemos ver como añadimos un botón configuramos el Icono a mostrar y nos suscribimos al evento Click.

Para terminar el ejemplo tenemos que “alimentar” al ThumbButtonManager con los mensajes que recibimos de Win32, para que pueda realizar sus tareas, es por eso que tenemos que llamar al método DispatchMessage para que pueda comprobar que se ha hecho clic en un botón.


Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Tags: , ,
Categorias: Windows 7 | .Net | WPF
Acciones: E-mail | Permalink | Comments (3) | Comment RSSRSS comment feed