Mostrando entradas con la etiqueta C#. Mostrar todas las entradas
Mostrando entradas con la etiqueta C#. Mostrar todas las entradas

viernes, 19 de diciembre de 2008

Esperas paralelas

La programación concurrente suele tener sus particularidades que hace que mucha gente la evite siempre que puede lo que es una lástima porque muchas cosas se pueden hacer mejor cuando se aprovecha la posibilidad de que un proceso ejecute paralelo al actual. Es también una lástima porque luego aparecen muchas apliaciones que a pesar de ser muy útiles hay momentos en que quedan paradas si hacer nada en lo que se ejecuta algún proceso que demora mucho y que tampoco podemos interrumpir.

Uno de los errores de implementación que mas me encuentro cuando alguien nuevo en la materia intenta programar algo en paralelo son las esperas. Necesitamos esperar generalmente porque estamos a espera de algo que no ha sucedido, como que esté disponible algún recurso o que algún proceso termine. Además del hecho mismo de esperar, el programador consciente se protege de la eventualidad de que aquello por lo que estamos esperando nunca suceda.

Aunque en .NET hay varias formas de implementar las esperas, aparentemente lo primero que se le ocurre a mucha gente es esperar durante un tiempo, probablemente porque es lo mas fácil y porque existen equivalentes cuando no se programa en paralelo. Generalmente esta espera viene en dos sabores:

El del programador inconsciente:

using System;

namespace paralelos
{
class Program
{
static volatile bool EsperaPorMi = false;

static void Main(string[] args)
{
// Mandamos a ejecutar un proceso paralelo
System.Threading.ThreadPool.QueueUserWorkItem(Paralelo);

// Supuestamente aqui hacemos otras cosas antes de decidir esperar
// ...

System.Threading.Thread.Sleep(6000); // Esta es la espera

// La finalización del proceo paralelo será marcada por el valor de la
// variable EsperaPorMi
if (EsperaPorMi)
Console.WriteLine("El proceso paralelo ha terminado");
else
Console.WriteLine("El proceso paralelo no ha terminado");
Console.WriteLine("\nPresione cualquier tecla para salir");
Console.ReadKey();
}

// Este es el proceso paralelo
static void Paralelo(object estado)
{
// Esto es para simular una operación que demora cinco segundos
System.Threading.Thread.Sleep(5000);
EsperaPorMi = true;
}
}
}


Aqui el programador inconsciente supone que sabe cuanto debe demorar la operación paralela y por lo tanto lo único que tiene que hacer es esperar un poco mas y asi garantiza que cuando deja de esperar la operación ya terminó.

Desafortunadamente esta solución tiene varios fallos obvios.
Para empezar, determinar con exactitud el tiempo que demora una sección de código de un programa es poco menos que imposible en el 95% de los casos en un sistema convencional. Estos tiempos dependen mucho de la configuración sobre la que ejecuta el programa ( procesador y sus nucleos, otros programas instalados y corriendo, procesos del sistema operativo, eventos que ocurran como la abertura de una torre de cd o un acceso remoto al pc, y un largo etcétera ). Si además la operación por la que estamos esperando tiene que acceder a recursos fuera de la gestión pura del programa ( archivos, bases de datos, etc. ) mas impredecible se torna. Finalmente y en caso de que la operación sea muy demorada ( y por tanto también la espera ) mas nada es hecho durante ese tiempo y por tanto se pierde en parte el beneficio de la ejecución paralela.

El programador consciente toma en cuenta los problemas anteriores y hace esto:

using System;

namespace paralelos
{
class Program
{
static volatile bool EsperaPorMi = false;

static void Main(string[] args)
{
// Mandamos a ejecutar un proceso paralelo
System.Threading.ThreadPool.QueueUserWorkItem(Paralelo);

// Supuestamente aqui hacemos otras cosas antes de decidir esperar
// ...

Console.WriteLine("Presione cualquier tecla para interrumpir");
// Ahora viene la espera
while (!EsperaPorMi)
{
System.Threading.Thread.Sleep(500); // Dormimos poco

if (Console.KeyAvailable) // El usuario se cansó de esperar ?
{
Console.Read();
break;
}
}

// La finalización del proceo paralelo será marcada por el valor de la
// variable EsperaPorMi
if (EsperaPorMi)
Console.WriteLine("El proceso paralelo ha terminado");
else
Console.WriteLine("El proceso paralelo no ha terminado");
Console.WriteLine("\nPresione cualquier tecla para salir");
Console.ReadKey();
}

// Este es el proceso paralelo
static void Paralelo(object estado)
{
// Esto es para simular una operación que demora cinco segundos
System.Threading.Thread.Sleep(5000);
EsperaPorMi = true;
}
}
}

En este caso el programador no pretende saber cuanto demora el proceso paralelo asi que en vez de esperar dormido durante tiempo suficiente lo que hace es verificar continuamente el estado de la variable de terminación y además verifica si el usuario pulsó una tecla indicando que ya no quiere esperar mas. Sin embargo, consciente como es, entre consulta y consulta duerme un poco, por que ?

El encargado de ejecutar las instrucciones de nuestro programa ( o mas estrictamente hablando , en lo que ellas se convierten ) es el procesador de la computadora. En un pc suelen haber en la mayoría de los casos de 1 a 4 procesadores o nucleos de procesadores con lo cual en cada momento solo pueden estarse ejecutando tantos procesos como procesadores x nucleos tengamos. Si nos damos un paseo por el task manager ( Ctrl+Shift+Esc ) y vamos a la pestaña de procesos vamos a ver que potencialmente hay muchos procesos que pueden necesitar de ejecutar y cada uno de ellos puede estar formado por muchos hilos cada uno de los cuales también necesita correr. El sistema operativo "planifica" al procesador para que le dedique un intervalo de tiempo a cada proceso/hilo y pasado ese tiempo pase a otro; eso ocurre muchas veces en cada milisegundo con lo cual nos da la idea de que se pueden ejecutar en paralelo mas procesos/hilos de los que serían posibles a juzgar por la cantidad de procesadores y nucleos que tengamos. Por lo tanto podemos concluir que el tiempo de procesador es un recurso bastante escaso, por eso el sistema no planifica al procesador ciegamente y una de las optimizaciones básicas que hace es no darle tiempo de procesador a quien no lo necesita, por lo tanto si un hilo está "dormido" por cualquier motivo el sistema no le dará tiempo de procesador con lo cual da la posibilidad de ejecutar otros que si lo necesitan. En el ejemplo anterior lo único que se hace entre espera y espera es ver si se ha precionado una tecla por lo tanto nos podemos tomar la libertad de dormir un poco ( 1/2 segundo ) antes de verificar el teclado y como el tiempo es tan corto el usuario ni se da cuenta. Si no lo hicieramos asi veríamos como aumenta el consumo de procesador con lo cual estamos ocupando sin motivos el tiempo que podría dedicarse a otro programa relentizando el performance general de la máquina.

Esta segunda solución es mejor que la primera y mas lógica, sin embargo hay casos en que ese medio segundo puede resultar mucho tiempo, especialmente si el programa ejecuta en un computador muy rápido. En esos casos no tiene sentido esperar mas de lo necesario y entonces lo que necesitamos es quedarnos dormidos y que sea el proceso paralelo el que nos despierte:

using System;

namespace paralelos
{
class Program
{
static System.Threading.ManualResetEvent EventoManual =
new System.Threading.ManualResetEvent(false);

static void Main(string[] args)
{
EventoManual.Reset(); // Nos aseguramos que no está señalizado
// Mandamos a ejecutar un proceso paralelo
System.Threading.ThreadPool.QueueUserWorkItem(Paralelo);

// Supuestamente aqui hacemos otras cosas antes de decidir esperar
// ...

Console.WriteLine("Presione cualquier tecla para interrumpir");
// Ahora viene la espera
while (true)
{
if (EventoManual.WaitOne(500)) // Dormimos solo lo necesario
{
// Si fue señalizado salimos con éxito
Console.WriteLine("El proceso paralelo ha terminado");
break;
}

if (Console.KeyAvailable) // El usuario se cansó de esperar ?
{
// No ha sido señalizado y el usuario ya no quiere esperar mas
// salimos sin éxito
Console.Read();
Console.WriteLine("El proceso paralelo no ha terminado");
break;
}
}

Console.WriteLine("\nPresione cualquier tecla para salir");
Console.ReadKey();
}

// Este es el proceso paralelo
static void Paralelo(object estado)
{
// Esto es para simular una operación que demora cinco segundos
System.Threading.Thread.Sleep(5000);
EventoManual.Set();
}
}
}


Aqui hemos quitado la variable de espera y la hemos sustituido por un evento manual. El evento tiene dos estados: señalizado y no señalizado. En su estado señalizado si llamamos al método WaitOne este retorna inmediatamente mientras que en el estado no señalizado el mismo llamado provoca que la ejecución quede bloqueada hasta que alguien ( otro proceso/hilo ) señalice al evento y desbloquee nuestra ejecución. Además el método WaitOne tiene la opción de pasarle el tiempo que queremos quedar bloqueados y si este pasa el retorna el valor false diciendo que nos desbloqueamos porque pasó el tiempo que le dijimos mientras que si fuimos desbloqueados por otro proceso retorna true.

En .NET tenemos muchas herramientas para tratar con los problemas que la concurrencia plantea, solo que es necesario saber usar los adecuados en casa caso.

sábado, 12 de abril de 2008

Pérdidas de memoria en .NET

Una de las mejores cosas de las plataformas "manejadas" como Java y .NET es la posibilidad de no tener que preocuparnos con los detalles del control del uso de la memoria, sin embargo hay situaciones que pueden provocar pérdidas de memoria si no las tenemos en cuenta como intenta explicar Calvin Hsia en Examine .Net Memory Leaks .

Este es un tema que muchas veces confunde, asi que me tomé la libertad de copiar su ejemplo y llevarlo a C#.


using System;

namespace Test
{
class Program
{
static void Main(string[] args)
{
var oldPeak = 0L;
for (int i = 0; i < 100; i++)
{
var oWatcher = new MyWatcher();
// oWatcher.UnSubscribe(); // Quitar comentario para quitar el controlador
oWatcher = null;
GC.Collect(); // Recolectar
GC.WaitForPendingFinalizers(); // Esperar por los finalizadores ( destructures )
GC.Collect(); // Recolectar los que tienen destructores
var newPeack = System.Diagnostics.Process.GetCurrentProcess().PeakWorkingSet64;
if (i > 0)
Console.WriteLine("Todo liberado? {0} WorkingSet ={1} Pico ={2} Delta ={3}",
i, System.Diagnostics.Process.GetCurrentProcess().WorkingSet64,
newPeack, (newPeack - oldPeak));
oldPeak = newPeack;
}
GC.Collect(); // Recolectar
GC.WaitForPendingFinalizers(); // Esperar por los finalizadores ( destructures )
GC.Collect(); // Recolectar los que tienen destructores
Console.ReadKey();
}
}

class MyWatcher
{
string[] _myLargeMemoryEater = new string[100000];
System.IO.FileSystemWatcher _fsw;
public MyWatcher()
{
_fsw = new System.IO.FileSystemWatcher();
_fsw.Path = "c:\\";
_fsw.Filter = "*.*";
_fsw.Created += _fsw_Created;
_fsw.EnableRaisingEvents = true;
}
public void UnSubscribe()
{
_fsw.Created -= _fsw_Created;
_fsw.Dispose();
}

void _fsw_Created(object sender, System.IO.FileSystemEventArgs e)
{
Console.WriteLine(e.FullPath);
}
~MyWatcher()
{
Console.WriteLine("Terminado en hilo "+System.Threading.Thread.CurrentThread.ManagedThreadId.ToString());
}
}
}


Al ejecutar el programa vemos como va consumiendo mas y mas memoria aun cuado en cada iteración estamos forzando la recolección de basura y la instancia que estamos creando no tiene aparentemente ninguna referencia. Aparentemente.

En realidad la referencia si existe porque al "enganchar" un evento a la clase FileSystemWatcher es como si estuviéramos enganchando un evento a un objeto del sistema operativo y mientras tal exista la referencia continuará ahi, no importa que no tengamos en el programa una referencia directa aparente al objeto.

Ahora si quitamos el comentario a la linea que dice
// oWatcher.UnSubscribe();

cada llamada al recolector de basura libera la memoria debido a que la llamada a UnSubscribe "desconecta" nuestro objeto del FileSystemWatcher, sin embargo es bueno notar que dos cosas se hacen aqui: una desconectar el evento, la otra liberar el FileSystemWatcher, por que ? El FileSystemWatcher como había dicho representa un objeto del sistema operativo, de hecho en su implementación interna debe tener un handle a un objeto del sistema operativo y no va a ser liberado hasta que se llame a Dispose, podemos hacer la prueba comentando esa linea y ver los resultados.

Como podemos ver aunque .NET maneja la memoria por nosotros, somos responsables cuando desde nuestro programa colocamos referencias a elementos externos, como en este caso objetos del sistema operativo.

domingo, 2 de marzo de 2008

Herencia múltiple en C# ( resuelta )

Bueno, en realidad no, no hay forma de resolver el tema de la herencia múltiple en C# sin modificar el lenguage, pero siempre podemos acceder a soluciones alternativas mas o menos funcionales para lograr los mismos resultados que queríamos obtener con la herencia múltiple.

Estas soluciones alternativas dependen en general de los medios que poseemos y los resultados esperados. En muchos casos el problema es que tenemos una clase utilitaria que espera objetos de un tipo determinado ( no una interface ), para que nuestro objeto pueda ser utilizado en ese contexto heredamos de aquel y listo:


// Este es el tipo que se usa en la clase utilitaria
public class Usable
{
public virtual void HacerAlgo()
{
Console.WriteLine("Hice algo");
}
}

// Esta es la clase utilitaria
public class Utilitaria
{
public void Usa(Usable u)
{
u.HacerAlgo();
}
}

// Y este es nuestro tipo
class NuestroTipo : Usable
{
public override void HacerAlgo()
{
Console.WriteLine("Hacer otra cosa");
}
}

Hasta aqui todo bien. Pero que pasa si nuestro tipo ya heredaba de otro ? Si hubiera herencia múltiple heredábamos de Usable y de nuestro tipo base pero como no hay siempre podemos instanciar un objeto de tipo Usable y publicarlo para luego usarlo:


class NuestroTipo : NuestroTipoBase
{
private Usable m_usa = new Usable();
public Usable Usar { get { return m_usa; } }
}

...

Utilitaria util = new Utilitaria();
NuestroTipo tipo = new NuestroTipo();
util.Usa(tipo.Usar);

Evidentemente este ejemplo tiene una falla fundamental: esto no tiene nada que ver con la herencia. Si queríamos usar la herencia es porque probablemente hay algo dentro del tipo Usable que queríamos reescribir asi que simplemente instanciarlo dentro del nuestro no resuelve ningún problema. Al final lo que queremos es redefinir el comportamiento del tipo Usable y además queremos en esta redefinición haga uso de las interioridades de nuestro tipo. La solución es crear una clase interna que herede del tipo Usable y que redefina lo que sea necesario, además sería necesario siempre pasarle una instancia de nuestro objeto para poder tener acceso a sus interioridades. Algo asi:


// Este es nuestro tipo que hereda de otro
public class NuestroTipo : NuestroTipoBase
{
// Este es nuestro usable interno
private class NuestroUsable : Usable
{
// Evidentemente necesitamos acceder a las
// interioridades del objeto master
NuestroTipo m_master;
public NuestroUsable(NuestroTipo master)
{
m_master = master;
}
public override void HacerAlgo()
{
Console.WriteLine("Hacer otra cosa ( internamente )");
}
}
private readonly NuestroUsable m_usa;
public Usable Usar { get { return m_usa; } }
public NuestroTipo()
{
m_usa = new NuestroUsable(this);
}
}

Esto resuelve el problema en parte, aunque nos deja todavía con la necesidad de instanciar el objeto, cosa que además tiene que hacerse en el constructor ya que no nos permiten instanciarlo en la declaración pasándole además this como parámetro.

Lo mejor evidentemente sería tener herencia múltiple nativa en el lenguage pero como no hay podemos acudir a este tipo de mecanismos para simularla cuando la necesidad se presenta.

viernes, 29 de febrero de 2008

Herencia múltiple

C# no tiene herencia múltiple, Java tampoco, Delphi tampoco. De los lenguages mas usados solo C++ tiene herencia múltiple y en la mayoría de los programas que he visto no es usada. Sin embargo es una de las características que mas extraño en mi viaje por .NET desde C++ porque ayuda a resolver muchos problemas. Me he encontrado conque quiero que una clase sea un componente por el uso que le voy a dar ( hereda de System.ComponentModel.Component ) pero además quiero que se beneficie de un mecanismo de persistencia que he creado. Al final terminas por hacer magia para que el mecanimo no heredado sea capaz de adivinar de todo ( reflection mediante ) o te creas una interface.

El método socorrido de la interface tiene la desventaja de que tienes que implementar los métodos de esta. En muchos casos instancias dentro de la clase un objeto que va a hacer el trabajo y los métodos de la interface solo llaman a los métodos de ese objeto pero esa solución es extremadamente trabajosa si la interface tiene varios métodos.

Sería interesante que se pudiera "delegar" en un objeto. Me gustaría poder hacer automáticamente lo que tanto he tenido que hacer a mano. Decirle al compilador que los métodos de la interface son implementados por un objeto de la clase. Eso en parte se puede lograr en .NET 3 con los métodos de extensión, pero solo en parte, se puede tener la funcionalidad agregada a la clase pero no se pueden aprovechar las ventajas de la herencia. Una solución podría ser en la definición de la clase decir como hasta ahora:

class Ejemplo : IEjemploInterface

pero cuando se declare el objeto interno que implementa los métodos de la interface decir algo como:

implements IEjemploInterface ObjetoInterface obj;

para decir que los llamados a los métodos de la interface se "delegan" en la instancia de ese objeto. Eso sería muy útil ... y me resolvería muchos problemas.

martes, 29 de enero de 2008

Cuando se habla de .NET 3 lo primero que salta a la mente es LINQ. Todo el mundo habla de LINQ como una maravilla de la técnica, y en realidad es bastante interesante.
Imaginemos que tenemos la clase Persona:

class Persona
{
string m_nombre;
string m_apellido;
public Persona(string nombre, string apellido)
{
m_nombre = nombre;
m_apellido = apellido;
}
public string Nombre { get { return m_nombre; } }
public string Apellido { get { return m_apellido; } }
public override string ToString()
{
return Nombre + " " + Apellido;
}
}

y tenemos una colección de personas:

List< Persona > personas = new List< Persona >();
personas.Add(new Persona("Rodrigo", "Rojas"));
personas.Add(new Persona("Armando","Fuentes"));
personas.Add(new Persona("Lucia", "Wong"));
personas.Add(new Persona("Maritza","Rojas"));

usando LINQ podríamos buscar a todos los "Rojas" asi:

var a = from p in personas
where p.Apellido == "Rojas"
select p;
foreach (Persona per in a)
{
Console.WriteLine("{0}", per);
}

Ya se que también podría preguntar en el ciclo, pero es evidente que se ve cool por un lado, por otro lado el ejemplo es idiotamente simplista pero las consultas que se construyen con LINQ pueden ser bastante mas complejas, sin contar con el hecho de que separar el ciclo de la selección tiene la ventaja de poder usar el mismo ciclo en selecciones diferentes, algo que ya se hace en .NET 2 con el yield return pero que aqui se lleva mas lejos con este mecanismo que permite estandarizar una buena parte de las búsquedas sobre colecciones, algo tan común en cualquier programa.

Con todo esto del LINQ no se notan los mecanismos verdaderamente poderosos que permiten que exista, estos son los métodos de extensión (extension methods) y el patrón consulta (query expression pattern).

Los métodos de extensión no son mas que una nueva forma de llamar a un método estático que permite que la invocación sea , quizas , mas clara. Por ejemplo: Vamos a suponer que queremos en el código anterior obtener el nombre de una manera mas ... formal, en vez de nombre+apellido, queremos apellido+","+nombre, pero supongamos que esa clase no fue escrita por nosotros y no podemos ir para atrás y modificarla por los motivos que fueran. Lo mas probable es que creemos un método estático en alguna clase, que si además solo hace eso también será estática, y que nos haga el trabajo, algo asi:

static class Nombrar
{
public static string NombreFormal(Persona per)
{
return per.Apellido + ", " + per.Nombre;
}
}

el ciclo anterior quedaría asi:

foreach (Persona per in a)
{
Console.WriteLine("{0}", Nombrar.NombreFormal(per));
}

Ahora convertimos NombreFormal en método de extensión con un ligero cambio:

static class Nombrar
{
public static string NombreFormal(this Persona per)
{
return per.Apellido + ", " + per.Nombre;
}
}

y el ciclo se puede escribir asi:

foreach (Persona per in a)
{
Console.WriteLine("{0}", per.NombreFormal());
}

Es todo una cuestión de imagen, pero queda sin duda mucho mejor.

Y ahora entra el patrón consulta. Este dice que si una clase implementa ciertos métodos entonces puede usarse en una expresión de consulta, como la que vimos al inicio, estos métodos son enrte otros Select, Where, Join, OrderBy , entre otros. Y que es LINQ ? Pues LINQ no es mas que la implementación de esos métodos para cualquier tipo que implemente la interface System.Collections.Generic.IEnumerable< t > por eso es que se puede usar con cualquier colección, hasta con un arreglo. Lo poderoso de esto es que uno puede hacer su propio LINQ, es decir, implementar estos métodos para hacer que nuestras clases se comporten como orígenes de datos "consultables".