Alters

Aventuras de DoHITB: parte III (b) - Final

Buenas!

Por fin llega el fin de semana, y tras una semana agotadora (13h/día trabajando) por fin me puedo relajar un poco y acabar (por fin) mi pequeña aventura.

Antes me gustaría comentaros que he avanzado mi proyecto de PPHP como parte del desarrollo en el que estoy metido. También tengo otra cosilla por ahí relacionada con una base de datos y PHP... algo así como un generador de objetos previamente definidos (aunque podría combinarlo con mi proyecto de PPHP y hacer algo verdaderamente paranóico...).

También aprendí a resolver un conflicto que siempre había tenido con JavaScript... esto lo veremos en la próxima entrada.

En fin, volvamos a la parte final de mi historia:

Como os comenté, vamos a ver Threads y peticiones HTTP.

Para usar estas características, nos apoyaremos en funciones ya establecidas de varios paquetes de JAVA. De todas maneras, vamos a ver cómo funcionan un poco por encima los Thread y las peticiones.

Thread

Un "thread" (hilo) es un fragmento de programa, un proceso si queréis, que se ejecuta de forma paralela al proceso (hilo) principal. Es decir: normalmente todos nuestros programas con concurrentes (un solo hilo); no obstante algunas partes podrían hacerse en paralelo (a la vez), y muchas veces no lo hacemos.

Un ejemplo sencillo: imaginad que "diseñamos una persona". Si usaramos programación concurrente, mientras respirara no podría parpadear (por ejemplo), por lo que tendríamos que hacer un bucle para que fuera respirando y pestañeando (incluso podríamos hacer que lo fuera haciendo "a trozos" para que pareciera que lo hace a la vez).

Sin embargo, en realidad lo que pasa es que son dos cosas completamente independientes. Pues sería como hacer un hilo (Thread) para la respiración y otro para el parpadeo.

Y bien, para usar concurrencia en programación tenemos que tener muy en cuenta el orden de procesos, ya que no sabemos a ciencia cierta qué hilo irá "más rápido".

¿A qué me refiero con eso? Con un ejemplo se ve mejor. Supongamos que quiero hacer lo siguiente::

a = 10;
b = 20;
c = 30;
d = 40;
e = 50;
f = 60;
g = 70;
h = 80;
i = 90;
j = 100;
k = 110;
c = a+b;
d = a-b;
d = d+b
e  = 100-d;
f = e*d*a;
c = c+100;
g = c+a;
h = c+b;
i = g*h;
j = i/f;
k = j+i/f;

He querido marcar un poco cosas que se pueden hacer concurrentemente  (del mismo color) y cosas que se pueden hacer paralelamente (diferentes colores). De esta manera, podríamos calcular "d" y "g" al mismo tiempo, ya que son independientes (no tienen datos en común); mientras que para generar "f", tenemos que tener "e", "d", y "a", por lo que estas cuatro variables tendrán que calcularse de manera concurrente entre ellas.

De manera gráfica, sería algo así:


Cada nodo (círculo) representa una operación, y aquellas que están bifurcadas pueden hacerse a la vez.

No obstante, como decía antes, debemos tener en cuenta el orden en que éstas se ejecutan, ya que se llegarámos a "i = g*h", siguiéramos  y todavía no se hubiera calculado "f = e*d*a", el valor de "i" sería distinto al esperado en la teoría.

Es por eso que tenemos los llamados "semáforos". Éstos, como en la vida misma, sirven para indicar cuándo un proceso tiene que esperar, para que todo vaya según lo planeamos.

En este ejemplo, en el caso planteado anteriormente, tendríamos al principio un semáforo con valor 0; luego en los nodos de "i" y "f", incrementaríamos el valor del semáforo en 1. Finalmente, en el nodo "j", haríamos que parara la ejecución hasta que el semáforo tuviera valor 2 (cuando ambas ramas se han terminado). Con los nodos "g" y "h" pasaría lo mismo.

También (como nos pasará en nuestro ejemplo) es posible que no haga falta semáforos.

Bien, ahora vamos con las peticiones:

Peticiones HTTP

En JAVA, tenemos los maravillosos buffer, que no son otra cosa que un "punto intermedio" entre dos componentes que pueden no ir a la misma velocidad.

Hay buffers en todas partes: en el teclado del PC, en la lectora de CD/DVD (algunos recordamos aquello de "buffering" cuando grabábamos un cd), en las descargas de vídeo (youtube, por ejemplo), y muchos otros sitios.

El buffer nació como solución a un problema, y es que cuando dos medios diferentes comparten información es difícil sincronizarlos.

Imaginaos que, de entre los 80 proceso que (fácilmente) tiene un ordenador, justo cuando tecleamos va la CPU y está ocupada con un proceso... constantemente perderíamos las pulsaciones. Por ello, se almacenan en un buffer, y son leídas de ahí por nuestro PC "cuando se puede" y se hace con ellas lo que se tenga que hacer.

Si queréis probar un efecto del buffer, probad a abrir un terminal (cmd, en Windows), y haced un "dir" en un directorio con muchos archivos. Mientras liste el "dir", escribid algo en el teclado...

Cuando acabe el dir, saldrá escrito aquello que hayáis escrito. Si no hubiera buffer, estas pulsaciones se hubieran perdido.

Pues bien, para hacer peticiones HTTP, creamos un buffer, para, "cuando se pueda" recoger la información.

Entonces, el proceso es el siguiente (simplificando):

 - Creamos el buffer
 - Le pasamos un objeto que no permitirá leer de ahí
 - Le damos una URL a la que hacer la petición
 - Vamos leyendo del buffer y almacenando en una variable
 - Cuando termine, devolvemos la variable

Bien, ahora vamos con un poco de práctica:

Para crear un hilo en java, debemos crear una clase que extienda la clase "Thread". Al extenderla, tendrá que implementar el método "public void run()", que es el método que "saltará" cuando llamemos al hilo.

Para llamar a un hilo, debemos hacer así:

MiHilo mh = new MiHilo(); //MiHilo es una clase que extiende a Thread
mh.start();
//seguimos con la ejecución normal, mientras mh va con su propia ejecución.

Debemos tener en cuenta que si hay datos compartidos en la teoría, ésto tiene que llevarse a la práctica.

El método que uso yo para esto es crear una clase "contenedor", que no tendrá más que todas las variables compartidas, declaradas como públicas. Luego, a las clases que extienden Thread les declaro un constructor en el que reciben un objeto de este tipo "contenedor", y sobre sus variables operamos.

Entonces, el proceso para dos hilos sería así:

Contenedor c = new Contenedor();
MiHilo hilo1 = new MiHilo(c);
MiHilo hilo2 = new MiHilo(c);
//ambos tienen la misma instancia de "Contenedor", y como JAVA pasa los parámetros por referencia (dirección de memoria, puntero), al modificar algo de "c" quedará cambiado en ambas variables (hilo1 e hilo2).

Por otra parte, para crear peticiones HTML, podemos fácilmente copiar éste método:

private String getHTML(String u) throws IOException{
URL url = new URL(u);
URLConnection con = url.openConnection();
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
StringBuffer sb = new StringBuffer("");
String aux;
while((aux=in.readLine()) != null) 
sb.append(aux);

return sb.toString();
}

En el que recibimos como parámetro la URL a la que queremos lanzar la petición, y recibimos como resultado el código HTML de ejecutar dicha petición, o una excepción si algo ha salido mal.

Me gustaría exponer el código que creé, pero he descubierto que hay un fallo con un Iterator (en el método que comprueba las peticiones fallidas), ya que la lista que itera está en continuo cambio (y lanza excepciones, entonces).

También me gustaría mejorar ese aspecto, pero no tengo tiempo para nada últimamente...

Si lo arreglo editaré la entrada o haré un anexo.

Entonces, la próxima entrada será sobre el tema JavaScript que tanto me fastidió en su día, y que hace un par de noches, estando tumbado en la cama, logré resolver: 

El solapamiento de eventos.

Y bueno, con esta aventurilla he cubierto (más o menos) el tema de "hacking" que tenía pensado publicar.

Me queda pendiente de explicaros algunas cosas interesantes de VB.net que pueden usarse con fructífero resultado, como el tratamiento de procesos combinado con las peticiones FTP... con un poco de investigación podemos conseguir algunas cosas...

En fin...

¡Hasta la próxima!