Mostrando las entradas con la etiqueta ciclo. Mostrar todas las entradas
Mostrando las entradas con la etiqueta ciclo. Mostrar todas las entradas

24 octubre, 2012

Modulador de Ancho de Pulso (PWM) en Java

javaPWM.png


JavaPWM es una sencilla aplicación que permite convertir cualquier PC con un puerto serial en un modulador de ancho de pulso (PWM).

Existen en internet varios circuitos simples que permiten controlar un relé con el puerto serial. JavaPWM aprovecha esta característica y envia pulsos al puerto serial que pueden servir para controlar un relé u otro dispositivo similar. He aqui un esquemático simple para controlar un relé por medio del puerto serial:

(originalmente publicada en http://www.windmeadow.com/node/4)

Permite configurar el puerto, modo de operacion, ciclo de trabajo y frecuencia. Se puede trabajar con un pulso simple de duracion determinada o bien como un oscilador a la frecuencia indicada.

El ciclo de trabajo (duty cycle) puede variar entre 1 y 99%. 

En modo oscilador (continuo) la frecuencia puede variar entre 1 y 50 Hz.

En modo pulso, el pulso puede durar entre 1 y 100 segundos.

El modo "off/on" invierte el ciclo.

Requiere Java 6 o superior y la biblioteca rxtx correctamente instalada o configurada.

En ciertos casos puede dejar el puerto bloqueado. En linux se desbloquea eliminando el archivo Lock del puerto en /var/lock. En Windows no tengo idea como desbloquear un puerto.

Para descargar la aplicación clic aqui. Es totalmente gratuita!
El codigo fuente se puede descargar desde aca.

Si te sirve, te gusta o tienes alguna sugerencia, tu comentario es bienvenido.



17 mayo, 2012

Tratamiento de ResultSet con multiples hilos

Aunque el título de la entrada no es el mas adecuado, la implementación de esta técnica permite procesar aparentemente un ResultSet con múltiples hilos.

Típicamente, en una aplicación que manipula una gran cantidad de datos con un ResultSet, tenemos el problema de que el tratamiento puede llegar a ser costoso en terminos de tiempo. Esto se debe a que el ResultSet no es thread-safe, es decir, no puede se accedido desde múltiples hilos a la vez.

Por lo tanto, un escenario como el que se ilustra abajo es técnicamente imposible:




Luego, se está obligado a utilizar un único hilo que acceda al ResultSet, lo que se traduce en un desperdicio de recursos y en un altísimo tiempo de espera en caso de contar con grandes volúmenes de información.

Una solución simple, es almacenar los datos en un objeto que actúe como buffer y que pued ser leído desde múltiples hilos. De este modo, se tiene por un lado a un hilo único accediendo al ResultSet y dejando los datos en nuestro buffer, y por otro lado a varios hilos que leen los datos de este buffer y los procesan en forma paralela.

Se llega a un escenario más cómodo, que permite el uso de múltiples hilos para el procesamiento de nuestros datos:



Para esto, se necesita un JavaBean que contenga los datos que son leídos desde el ResultSet, una cola para almacenar estos JavaBeans a medida que se van leyendo y una clase que extienda Thread (o implemente Runnable) que será la encargada del proceso. 

La secuencia sería más o menos así:
  • Obtener el ResultSet
  • En un hilo, leer el ResultSet, crear los Beans de Datos y llenar el buffer.
  • Lanzar uno o varios hilos que lean el buffer.
Adicionalmente se necesitan algunos mecanismos de control para:
  • Ganatizar que el Buffer no crezca descontroladamente.
  • Asegurar que los hilos de proceso se esten ejecutando hasta que se haya leido completamente el ResultSet.
  • No continuar hasta que todos los hilos de proceso hayan terminado (este paso puede ser opcional).

Existen muchas formas de implementar esta lógica. Aqui propongo una simple, que puede ser mejorada en gran medida.

Primero: Definimos una clase que actue como "Bean" de datos:

class Bean {
        
        private String dato1;
        private String dato2;
        private String dato3;

        public String getDato1() {
            return dato1;
        }

        public void setDato1(String dato1) {
            this.dato1 = dato1;
        }

        public String getDato2() {
            return dato2;
        }

        public void setDato2(String dato2) {
            this.dato2 = dato2;
        }

        public String getDato3() {
            return dato3;
        }

        public void setDato3(String dato3) {
            this.dato3 = dato3;
        }
        
    }

Luego, definimos una clase donde se hará el proceso:



class Proceso {
    
    private static final int MAXIMO_BUFFER = 10;
    private static final int MAXIMO_HILOS_PROCESO = 5;
    private ResultSet rs;
    private Thread[] hilosProceso;
    private volatile boolean leyendoRs;
    private LinkedBlockingQueue buffer;

    /**
     * Este metodo debe ser lanzado desde un hilo que no sea el 
     * event-dispatch-thread
     */
    public void procesar() throws SQLException {
        buffer = new LinkedBlockingQueue();
        hilosProceso = new Thread[MAXIMO_HILOS_PROCESO];
        rs = leerBaseDatos();
        leyendoRs = true;
        Runnable proceso = new Runnable() {

            @Override
            public void run() {
                while (true) {
                    Bean b = buffer.poll();
                    if (b == null) {
                        if (leyendoRs) {
                            try {
                                Thread.sleep(250);
                            } catch (InterruptedException ex) {
                                ex.printStackTrace();
                            }
                        } else {
                            break;
                        }
                    } else {
                        //Hacer algo con Bean b
                    }
                }
            }
        };
        for (int i = 0; i < MAXIMO_HILOS_PROCESO; i++) {
            hilosProceso[i] = new Thread(proceso, "Hilo Proceso " + i);
            hilosProceso[i].start();
        }
        
        while (rs.next()) {
            Bean b = new Bean();
            b.setDato1(rs.getString("dato1"));
            b.setDato2(rs.getString("dato2"));
            b.setDato3(rs.getString("dato3"));
            buffer.offer(b);
            if (buffer.size() >= MAXIMO_BUFFER) {
                try {
                    Thread.sleep(250);
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
            }
        }
        rs.close();
        leyendoRs = false;
        
        for (Thread hilo : hilosProceso) {
            if (hilo.isAlive()) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
            }
        }
        
        
        /*
         * En este punto estamos completamente seguros de que 
         * todos los datos han sido procesados.
         * 
         */
    }

}

Como puede verse, la clase que procesa los datos es bastante simple. Utilizamos como buffer la clase LinkedBlockingQueue que permite un acceso concurrente a los metodos offer() y poll(). 

Obetenemos los datos con algún método a nuestro gusto, y luego levantamos un flag que indica que estamos leyendo los datos. 

Utilizamos la interfaz Runnable y definimos una pequeña clase anónima que procesa los datos. Esta clase está constantemente leyendo los datos desde el buffer. Si en algún momento no se han leído datos, pero el flag de lectura aún está a true, el hilo esperará 250 milisegundos y continuará su lectura. Esto permite que no se detengan los hilos aunque se produzca algun retraso en el llenado del buffer.

El bloque while que lee el RsultSet crea las instancias de nuestro bean de datos y las encola. Contiene un pequeño bloque de control, el cual verifica que si el tamaño del buffer supera un máximo establecido, se esperan 250 milisegundos antes de continuar. Esta tecnica me ha dado mejores resultados que definir el tamaño máximo de LinkedBlockingQueue (se me estaban perdiendo datos). Cuando termina el ciclo de lectura del ResultSet, el flag de lectura se lleva a false, lo que indica a los hilos de proceso que ya no habrá mas datos para procesar.

Finalmente, existe un pequeño bloque de control, el cual verifica que mientras existan hilos de proceso activos, se esperará. Por lo tanto en este punto la ejecución no continuará hasta que todos los hilos hayan finalizado.

El código es bastante rudimentario, pero es efectivo. Pueden hacerse varias mejoras en las secciones de control (sobre todo la del final), pero prefiero publicar este código ya que es simple de entender. Todas las mejoras y extensiones quedan a gusto del consumidor ;-)


bytes!




30 agosto, 2011

Recorriendo Vectores gigantes con multiples hilos

Hay momentos en la vida en que inebitablemente nos enfrentamos a la busqueda de un valor dentro de una coleccion gigante. Hay veces en que el uso de un contains(Object o) aplicado a una Collection puede ser suficiente, pero hay ocasiones en las que incluso esto no es bastante.
Hace poco me vi enfrentado a un problema en que dado un dato y un gran vector, debía aplicar un algoritmo de comparación especial que me impedía recurrir al contains().
El problema fundamental en este caso era que la comparación no se hacía directamente con un metodo equals() o un == sino que ambos datos debían ser entregados a un tercer objeto que se encargaba de la verificación, por lo que me vi enfrentado a un punto en el que mi unica vía era iterar uno a uno los elementos del vector.
En las pruebas de desarrollo el modelo funcionaba perfectamente, pero al ponerlo en producción comenzó a mostrar falencias en el rendimiento. Pese a que el algoritmo de verificación es eficiente, el ciclo for utilizado podía ser realmente demoroso.
Lugo de pensarlo un poco, llegué a una solución bastante aceptable, aprovechando las características multi hilo de Java.
La solución en si es simple: divide y vencerás. Corté el vector en N trozos de pequeño tamaño e inmediatamente lanzo N hilos de verificación simultánea que realizán la búsqueda. El primero que encuentre el dato grita "gané" y detiene a los demas hilos.


Visto paso a paso:


  1. Establezco un tamaño de bloque. Por ejemplo 100 unidades. Cada hilo hará la búsqueda sobre este número de datos.
  2. Divido el tamaño de mi vector en el tamaño del bloque. Si el resultado es menor a uno, significa que mi vector es menor al bloque, por lo que el resultado debe ser 1.
  3. Preparo un espacio para que cada hilo notifique que ha terminado sin encontrar resultados.
  4. Preparo un boolean para indicar que el dato fue encontrado.
  5. Lanzo hilos de busqueda.
  6. Espero...

La implementacion mas simple de esta algoritmo, podria hacerse asi:

public class Buscador  {

    private static final int TAM_BLOQUE = 100; //Tamaño del bloque
    private static Logger logger = Logger.getLogger(Buscador.class.getName()); 
    private boolean encontrado;                //Indica que el dato fue encontrado
    private List datos;                  //Donde se hara la busqueda
    private Map flags;       //Donde cada hilo indica que termino
    private Resultado resultado;               //El dato que fue encontrado 

    public  Buscador(List datos) {
         this.datos = datos;
     }

    /**
     * Este metodo debe ser lanzado desde un hilo, no desde el event-dispatch 
     * thread ya que si no bloqueara la GUI
     *
     */ 
    public Resultado buscar(Muestra muestra) {
        if (datos == null || datos.isEmpty()) {
            return null;
        }
        //Sincronizo los datos para buscar desde muchos hilos 
        List vector = Collections.synchronizedList(datos);
        int bloques = datos.size() / TAM_BLOQUES;
        if (bloques < 1) {
            bloques = 1;
        }
        flags = Collections.synchronizedMap(new HashMap(bloques)); 
        final int largoSegmento = t / bloques;
        encontrado = false;

        resultado = null;
        /*
         * Este es el punto donde se lanzan los hilos de busquedas. Aprovechamos 
         * las caractristicas de las clases anonimas y el metodo sleep() de la 
         * clase Thread. Por eso este metodo debe lanzarse desde un hilo en 
         * segundo plano. 
         */ 
         for (int hilo = 1; hilo <= bloques; hilo++) {
             final int nHilo = hilo;  //Lo hacemos final para usarlo en la clase anonima
             flags.put(nHilo, false); //El hilo aun no termina
             Runnable busqueda = new Runnable() {

                 public void run() {
                     //Desde donde y hasta donde debe buscar este hilo 
                     int desde = desde = (nHilo - 1) * largoSegmento;
                     int hasta = largoSegmento * nHilo;

                     /*
                      * Instancia del verificador. Es propia de cada clase,  
                      * para asi evitar colisiones entre los hilos
                      */
                      Verificador v = Verificador.newInstance();
                      for (int k = desde; k < hasta; k++) {
                          if (encontrado) {
                              logger.debug("Hilo " + nHilo + ": Otro hilo encontro el registro :(");
                              marcarfin();
                              return;
                          }

                          Object dato = vector.get(k);
                          //Realiza la verificacion. . .. bla bla bla 
                          v.verificar(dato, muestra); 
                          if (v.esValido()) {
                              logger.debug("Hilo " + nHilo + ": Encontre el registro! :D");
                              encontrado = true;

                              resultado = v.getResultado();
                              marcarfin();
                              return; 

                          } 
                      } 
                      logger.debug("Hilo " + nHilo + ": Finalizo sin encontrar registro :(");
                      marcarfin(); 

                 }
                 
                 /**
                  * Coloca en el mapa la marca de que este hilo ya temino.
                  */ 
                 private void marcarfin() {
                     flags.put(nHilo, true);
                 } 

             };
             new Thread(busqueda, "Hilo de busqueda " + hilo).start();
         } 
         while (!encontrado && !finalizado()) {
             try {
                 Thread.sleep(500L);
             } catch (InterruptedException ex) {
                 //algo con la excepcion
             }
          }
         return resultado; 

    } 


    /**
     * Verifica que todos los hilos hayan finalizado. 
     */ 
    private boolean finalizado() {
        for (boolean hiloFinalizado : flags.values()) {
            if (!hiloFinalizado) {
                return false;
            }
        }
        return true;
    } 


} 


 A simple vista puede verse algo confuso, pero si examina con detencion, puede verse que el truco se encuentra dentro del ciclo for. Aqui se lanzan tantos hilos como deban ser lanzados. Cada hilo comparte algunos atributos con los demas hilos: Un mapa para grabar su estado, la variable donde se graba el resultado, un boolean para indicar que el dato fue encontrado y un vector sincronizado que es copia del original.

Cada hilo realiza la búsqueda en su espacio asignado dentro del gran vector. Asi, por ejemplo si nuestro vector tiene 1000 unidades y cada bloque es de 100, el primer hilo hará la busqueda desde el 0 al 99, el segundo desde el 100 al 199, el tercero desde el 200 al 299 y asi sucesivamente.

Obviamente los valores aqui expresados son para ejemplo, quiza puedan modificarse los valores para ajustarse a un escenario más real. La clase Verificador y sus metodos son ficiticias y la idea es que sea reemplazado por lo que se necesite. Del mismo modo puede optimizarse el uso de Generics en el buscador y hacer otras mejoras como eliminar el boolean "encontrado".

También puede limitarse el número de hilos lanzados aplicando un mecanismo de pausa y espera  para el control de los hilos y varias mejoras más que se me van ocurriendo.

Actualmente esta implementación está funcionando perfectamente y mis usuarios no han vuelto a quejarse por lentitud.

=) 

19 agosto, 2009

Control y pausa de ciclos for, do, while en Java

Un problema que sucede a menudo cuando se necesita procesar gran cantidad de informacion en un ciclo for o do/while es el de pausar el ciclo, esperar alguna señal y continuar.

for (condicion) {
    ....hacer algo...
    ....esperar....
    ....continuar.....
}

Por ejemplo, un problema al que me vi enfrentado fue el de ingresar el precio para una gran cantidad de productos, teniendo los productos en un Collection, parecia facil iterar sobre este listado, mostrar una ventana para que el usuario ingresara el precio y continuar:

Collection productos;
for (Producto p : productos) {
    ingresarPrecio(p);
}


A primera vista se ve simple, pero si este codigo es ejecutado, su resultado sera abrir tantas ventanas de ingreso como productos tenga el Collection.

La forma de ir mostrando las ventanas de forma ordenada, es pausar el ciclo for hasta que la ventana genere algun evento. Para ello lo primero sera hacer correr el ciclo dentro de un Thread diferente del actual de forma que pueda ser controlado sin interrumpir la ejecucion del programa:
Runnable hiloFor = new Runnable() {

    public void run() {
        for (Producto p : productos) {
            ingresarPrecio(p);
        }
    }
}

new Thread(hiloFor).start;


Esto lanzara nuestro ciclo for en un hilo distinto del actual, abriendonos la posibilidad de control. Para ello utilizaremos los metodos proporcionados por la clase Thread.

Lo que haremos a continuacion sera sincronizar el Thread donde vive nuestro ciclo for con el Thread principal donde se enta ejecutando nuestra aplicacion (en el caso de una aplicacion Swing sera el Event dispatching thread
Collection productos;
Thread hilo;

Runnable hiloFor = new Runnable() {

    public void run() {
        Thread actual = Thread.currentThread();
            synchronized(actual) {
                for (Producto p: Productos) {
                    ingresarPrecio(p);
                    actual.wait();
                }
            }
        }
}

hilo = new Thread(hiloFor);
hilo.start();


Puede notarse de inmediato los cambios introducidos: Primero obtenemos el objeto Thread donde se ejecuta nuestro for y luego ejecutamos el ciclo dentro de un bloque sincronizado con este hilo, invocamos el metodo que abre la ventana y pausamos la ejecucion del hilo.

En este punto el hilo donde se ejecuta el ciclo for estara pausado de forma indefinida esperando la señal notify(). Luego el metodo ingresarPrecio(Producto p) debera:


  • Abrir la ventana para ingresar el precio en el event dispatching thread

  • Notificar al hilo que puede continuar una vez se haya hecho el ingreso


Para ello, bastara que el metodo ingresarPrecio(Producto p) incluya en su interior algo similar a:


ingresarPrecio(final Producto p) {
    Runnable abrir = new Runnable() {

        public void run() {
            VentanaIngreso v = new VentanaIngreso(p);
            v.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    continuar();
                }
            });

        }

        private void continuar() {
            synchronized (hilo) {
                hilo.notify();
            }
        }
    }

    SwingUtilities.invokeLater(abrir);
}


La parte importante aca es el metodo continuar(). Aqui se le dice al hilo que continue ejecutandose. Como puede verse en este sencillo ejemplo, este metodo se llama desde un ActionListener que sera gatillado dentro de la ventana de ingreso ante ciertas acciones (cancelar, grabar el precio, etc).

El ejemplo puede mejorarse y extenderse a otras situaciones.


Bytes!