Multitaréa e Hilos, fácil y muchas ventajas


La descripción que te dispones a leer, pese a su carácter sencillo e ilustrativo tiene un alto contenido informativo. El autor considera que, lo aquí abajo escrito, pueda que no sea comprensible para quien todavía no tenga clara la orientación a objetos (recomiendo leer antes este otro tutorial). Y sin más dilación…

Existe una ley informática llamada ley de Wirth que reza (extraída de http://es.wikipedia.org/wiki/Ley_de_Wirth):

“El software se ralentiza más de prisa de lo que se acelera el hardware”

Y sintiéndolo mucho, tengo que darle la razón, porque los programadores de aplicaciones, en la mayoría de los casos, no sabemos emplear la potencia del hardware y no se optimiza. Este artículo intentará solventar una gran parte.

Hacer varias cosas de manera simultánea, que gran regalo y que lio ¿de verdad? No, solo hay que entender en que se basa y como se programa.

¿Regalo? Puede que también caigas en la pregunta ¿Para qué queremos la multitarea? Una respuesta rápida sería: para que el usuario no se enfade con nosotros, por esperar demasiado, en momentos en los que no hace falta (Seguro recuerdas cualquier pantalla de “cargando…” o en inglés “Loading…”, ¿Seguro que era necesaria?); notar que nos referimos a la inutilidad de la mayoría de las veces de bloquear al usuario, no a la ejecución que se produce debajo de esa pantalla de carga.

Otro efecto molesto y estarás de acuerdo conmigo, es aquel que se da cuando el usuario pulsa un botón y la aplicación se bloquea, pero no se bloquea por un error, sino porque debajo se está ejecutando un montón de código que requiere tiempo de proceso para concluirse (sí, no te rías por recordar alguna que otra vez algún bucles «for» o «while», que tardaba más de la cuenta en terminar y se notaba; lo hemos hecho todos 😉 ). Todo esto se soluciona con la multitarea ¿Todo? Sí, pero puede que antes haya que pensar un poco para caer en la solución con ésta. Con la multitarea el usuario nunca quedará bloqueado -pudiendo seguir usando la aplicación mientras algo muy gordo se ejecuta debajo; se eliminan la mayoría de pantallas de carga o no interrumpirán la experiencia de uso de la aplicación; y se ejecutará de manera más óptima, haciendo que el procesador no esté esperando continuamente y con cuellos de botella por llegarle un montón de cosas a la vez. Claro está, si se hace bien.

Es muy importante entender bien como se controla la multitarea, pues es la única manera que tenemos de desgranar el código de una aplicación, y que se ejecute en menos tiempo o para no bloquear al usuario.

El concepto de Hilos (en inglés Thread) nos ayuda. Un Hilo es un trozo de código de nuestro programa que puede ser ejecutado al mismo tiempo que otro. ¿Qué puede ejecutarse de manera simultánea a otro? Vamos a poner el siguiente ejemplo, imagina que queremos ver un listado de 100 imágenes que se descargan desde Internet, como usuario ¿Cuál de las dos opciones siguientes elegirías?:

A)     Descargar las imágenes 100 imágenes, haciendo esperar al usuario con una pantalla de “cargando” hasta que se descargan todas. Luego podrá ver el listado con las imágenes.

Descargar datos de Internet sin multitaréa - www.Jarroba.com

B)     Que mientras se descargan las 100 imágenes, el usuario pueda ir viendo y usando las que ya se han descargado.

Descargar datos de Internet con multitaréa - www.Jarroba.com

Como desarrollador la opción A es más sencilla ¿Seguro? Pero no te he preguntado cómo desarrollador, te he preguntado lo que un usuario preferiría. La B es lo que todo usuario quiere de una aplicación, tener que esperar no es una opción. Volviendo al punto de vista del desarrollador, tampoco es una opción. Un buen desarrollador de aplicaciones hace bien las cosas y se decanta por la opción B –la opción A no existe, la opción A nos lleva de cabeza a la “ley de Wirth” antes descrita- queremos ser profesionales y la opción B nos llena de gozo.

Desde el punto de vista del usuario existen dos áreas bien diferenciadas, que el desarrollador ha de tener en cuenta:

  • Primer plano: Aquí se ejecuta únicamente un hilo llamado “hilo principal”. Aquí hemos programado siempre, sin conocimiento de que estábamos trabajando ya con hilos. Es el hilo que trabaja con las vistas, es decir, con la interfaz gráfica que ve el usuario: botones, ventanas emergentes, campos editables, etc. También, puede ser usado para hacer cálculos u otros procesamientos complejos, aunque estos deberían de evitarse hacerse en este hilo a toda costa –salvo si es imposible que se hagan en otro hilo. Cabe señalar, que el primer plano influirá en la felicidad del usuario con nuestra aplicación. Aquí es donde el usuario interacciona de manera directa, además todo lo que pase aquí lo ve y lo siente. El desarrollador ha de tener especial cuidado al trabajar con el hilo principal, pues será juzgado por el usuario –si la aplicación va lenta es porque el primer plano va lento y esto al usuario no le gusta nada. También es importante saber, que una mala gestión del primer plano por parte del desarrollador, será castigada por el sistema operativo (por ejemplo: en Android si el hilo principal de una aplicación es bloqueado más de 5 segundos, la aplicación se cerrará mostrando una ventana de forzar cierre; y seguro que recuerdas comportamientos parecidos en otros sistemas operativos cuando te dice que “la aplicación no responde, ¿deseas finalizar su ejecución?”).
  • Segundo plano (o en inglés background): Se ejecuta todo el resto de hilos. El segundo plano tiene la característica de darse en el mismo momento que el primer plano. Aquí los hilos deberían de llevar las ejecuciones pesadas de la aplicación. El segundo plano el usuario no lo ve, es más, ni le interesa, para el usuario no existe. Por lo que comprenderás, que el desarrollador puede moverse libremente por este segundo plano –dentro de unos límites. Aquí el desarrollador se puede resarcir y hacer que, por ejemplo, un método tarde horas en ejecutarse, ya que el usuario ni lo sentirá –aunque si está esperando sí que lo notará, con lo que un poco de cuidado también.

Lo principal es claro: no debemos interrumpir al usuario nunca. Por lo que: no debemos hacer cosas que consuman muchos recursos en el hilo principal, hilo que corre en primer plano. Realmente, una vez que entiendas al 100% cómo funcionan los hilos, casi todo nuestro programa debería de ejecutarse en hilos bien separados y cuanto más pequeños mejor.

Antes de continuar, vamos a notar una cosa que suele suscitar dudas: no confundamos el término proceso con hilo. Un proceso es el programa o aplicación en ejecución (Extiendo un poco más para que queden claras las diferencias. Lo que llamamos aplicación es el fichero ejecutable almacenado en memoria. Varios procesos pueden ejecutar varias instancias del mismo programa, es decir, como cuando se abren varias ventanas de un Bloc de notas o un Word). Así, se deduce y es verdad que un proceso contiene un hilo –mínimo el hilo principal que corre en primer plano- o varios hilos -El principal más algunos en segundo plano.

Para no liarnos con los hilos (vago chiste, lo sé), vamos a ver un ejemplo a modo de comic. Retomemos el ejemplo anterior: un listado de imágenes que se descargan desde Internet. Para hacerlo más cómodo y comprensible, vamos a ejemplificarlo con personas. Una persona es un hilo, como el siguiente:

Generalidad de un hilo que lo hace todo - www.Jarroba.com

Este honorable trabajador (nuestro hilo), hace su trabajo de la mejor manera que sabe, pero tiene demasiadas responsabilidades. Para lo que le han contratado es para llevar paquetes desde una fábrica hasta el escaparate de una tienda. Además, este trabajador/hilo es el hilo principal de la aplicación.

Leyenda de la abstración del trabajo con threads - www.Jarroba.com

Veamos comodamente, los pasos en modo de comic, de todo el proceso de este trabajador/hilo, con sus correspondientes equiparaciones en la vida de un programa. Un inciso antes de continuar, el trabajador tiene un cerebro ¿Ya lo sabías no? Lo sepas o no da igual, porque de momento no vas a hacer ni caso al cerebro en las siguientes imágenes, el significado del cerebro se revelará más adelante; de momento me conformo con que te creas que tiene un cerebro y al cerebro ni caso 😀 . A continuación el ejemplo, la palabras raras del título se explicarán también más adelante, es importante comprender primero como trabajan los hilos y luego el  significado de las palabras raras vendrá solo. El ciclo completo de lo que este trabajador/hilo hace es:

Ejemplo sin concurrencia ni paralelismo, es decir, sin multitaréa

1)      Primero busca todos los paquetes de datos con las “imágenes” de una fábrica que se llama “Internet”.

No multitaréa No concurrente No paralelo 1 de 4 - Descarga de datos de Internet con el hilo o thread principal - www.Jarroba.com

2)      Lleva todos estos paquetes en camión y los descarga a una tienda llamada “dispositivo del usuario”.

No multitaréa No concurrente No paralelo 2 de 4 - Llevar datos al dispositivo con el hilo o thread principal - www.Jarroba.com

3)      Luego le toca colocar todo y cada uno de los paquetes que ha trasportado al escaparate de la tienda, que para aclarar también podemos llamar como “pantalla del dispositivo”.

No multitaréa No concurrente No paralelo 3 de 4 - Mostrar los datos en pantalla con el hilo o thread principal - www.Jarroba.com

4)      Todo esto se hace con la finalidad de que el cliente o “usuario” los consuma. Eso sí, solo en este momento se le permite al usuario poder consumirlos –ya que es el momento que tiene algo que consumir, antes no había nada- con lo que el resto del tiempo estuvo esperando.

No multitaréa No concurrente No paralelo 4 de 4 - Termina de procesarse el hilo o thread principal - www.Jarroba.com

 ¿Un emoticono triste, tirando a muy enfadado? Sí, el usuario lleva bastante tiempo enfadado con nuestra aplicación. Ha tenido que esperar demasiado y además cuando no hacía falta. Un usuario descontento se traducirá en menos estrellas en nuestra aplicación, en malas críticas, o en otras maneras de expresar una opinión negativa hacia nuestro programa.

Seguro que te has fijado en el doble color de nuestro trabajador/hilo que es un hilo principal todo él. Tiene dos colores para representar la gran carga de trabajo que tiene este hilo. Habrá que investigar si se puede hacer de él dos hilos diferentes ¿Será necesario contratar a un segundo trabajador/hilo? Espero que respondas afirmativamente, pues es algo que podría hacerse en dos hilos diferentes, uno principal –encargado de dibujar en pantalla- y otro en segundo plano –encargado de descargar los datos desde Internet.

Dos hilos o threads uno en primer plano y otro en segundo plano - www.Jarroba.com

Teniendo dos trabajadores/hilos es lógico pensar que todo vaya mejor y más rápido (¡Para eso hemos contratado más manos de obra!). Dispondremos de un trabajador/hilo principal cuya dedicación es exclusiva para el usuario. Y otro hilo/trabajador en segundo plano, que se dedica a descargar los paquetes de datos, cuya función no la siente el usuario y nunca sabrá que está ahí trabajando para él. Vuelvo a notar en este ejemplo, de todavía no hacer caso a la imagen del cerebro, ya queda poco para hablar de este órgano de proceso (es una pista de qué tratará, pero no nos preocupa ahora, sino un poco más adelante). Por lo que la manera de procesar estos trabajadores/hilos será el siguiente:

Ejemplo de concurrencia y paralelismo, la multitaréa en todo su esplendor

1)      Para empezar, el trabajador/hilo en segundo plano toma algún paquete de datos de la fábrica que llamamos “Internet”, lo lleva en camión y lo descarga en la tienda. Donde el trabajador/hilo principal está esperando a que le llegue algún paquete para poder realizar su labor.

Multitaréa concurrente y paralelo 1 de 4 - Descarga de datos de Internet con el hilo o thread secundario mientras el principal espera contenido- www.Jarroba.com

2)      El trabajador/hilo principal ya tiene algo que hacer. Toma el paquete que le ha llegado y lo coloca en el escaparate de la tienda (que hemos llamado casualmente como “pantalla del dispositivo”). Mientras tanto, el trabajador/hilo en segundo plano no ha terminado su trabajo, con lo que continúa llevando paquetes a la tienda desde la fábrica. Cabe notar, que ha sido dicho y hecho, el cliente/usuario ya tiene al menos un paquete que consumir en la tienda.

Multitaréa concurrente y paralelo 2 de 4 - Descargar datos de Internet con el hilo o thread secundario mientras el principal los coloca en la interfaz- www.Jarroba.com

3)      El proceso continúa. El trabajador/hilo en segundo plano lleva a la tienda los paquetes, para que el trabajador/hilo en primer plano los coloque en el escaparate. Por lo que, el cliente/usuario puede ir consumiendo cuanto desee de lo que exista ya en el escaparate.

Multitaréa concurrente y paralelo 3 de 4 - Tanto el hilo o thread principal como el secundario siguen trabajando, el usuario puede ya usar lo que hay- www.Jarroba.com

4) Esto durará hasta que se cumpla alguna condición de finalización o interrupción de algún trabajador/hilo, las cuales pueden ser muchas (por ejemplo: apagar el dispositivo, que se hayan acabado los paquetes que descargar, etc).

Multitaréa concurrente y paralelo 4 de 4 - Siguen los dos hilos o threads ejecutándose hasta terminar, el usuario puede usar más datos descargados mientras - www.Jarroba.com.png

Con los hilos aparece la verdadera magia: la concurrencia ¿Concurrencia? Y con ello se posibilita la programación en paralelo ¿Paralelo? Ya empezamos con los términos raros de la multitarea en todo su esplendor. Tranquilo con las palabras extrañas, no hace falta que te las aprendas, pero al final te las terminaras sabiendo por repetición y por aplastante lógica.

Te acuerdas del primer ejemplo de la fábrica y la tienda (recordar el «Ejemplo sin concurrencia ni paralelismo, es decir, sin multitaréa»), en el que solo había un hilo principal que lo hace todo. En este ejemplo tenemos que todo se hace en un tiempo X que se estima largo. Podemos representar por la siguiente barra de tiempo desde el principio de la ejecución del hilo hasta el fin:

Tiempo en ejecutarse un hilo o thread principal que tiene gran carga de trabajo - www.Jarroba.com

En comparación con este segundo ejemplo, en el que ya tenemos dos hilos, uno principal y otro secundario, que trabajan de manera simultánea. Es deducible que el tiempo que tarda en hacerse todo el proceso es bastante menor, ya que se sobrexponen los tiempos de ambos hilos. En la siguiente representación vemos como el hilo principal se ejecuta de manera “paralela” al hilo en segundo plano y que es menos a la anterior barra de tiempo:

Tiempo en ejecutarse dos hilos o threads, el principal y el secundario, que se reparten la carga de trabajo - www.Jarroba.com

Esto nos concluye en la definición de paralelo: dos o más hilos que se ejecutan de manera simultánea.

¿Pero todos los hilos que pueden ejecutarse en paralelo se ejecutan todos a la vez? No siempre. Depende de un factor delimitador más a tener en cuenta, que aunque no lo controla el desarrollador de aplicaciones existe siempre –es más, al desarrollador de aplicaciones no le importa demasiado, pero conocerlo explica muchas cosas y es muy útil para entender bastante, por lo que en Jarroba.com te ayudaremos a entenderlo.

Inundémonos de un conocimiento que seguro ya hemos escuchado docenas, que digo docenas miles de veces: los núcleos de un procesador. O sí, cuántas veces nos habrán vendido un ordenador o móvil por los núcleos de su procesador; convenciéndonos de que así irá más rápido, adornado de la palabra “multitarea”. ¡Qué decepción!, la mayoría de las aplicaciones no los aprovechan porque los desarrolladores de aplicaciones no los han tenido en cuenta, no han sabido aprovechar la tecnología. Pero tú, atento lector, sabrás como aprovechar la última y máxima tecnología para tus futuros desarrollos.

Bueno, sabiendo que un procesador es el encargado de ejecutar los programas. Y que un procesador puede tener entre un núcleo y muchos.

Procesador con dos núcleos - www.Jarroba.com

Sirviendo los núcleos para procesar de manera simultánea varios hilos (he aquí otra vez el amigo hilo). Esto es, cada núcleo ejecuta un hilo a la vez. Por lo que si tenemos un procesador con dos núcleos, podremos ejecutar dos hilos a la vez. Si tenemos uno de cuatro núcleos podrá ejecutar un máximo de cuatro hilos de manera simultánea. Y así con todos los núcleos de los que dispongamos.

Para seguir con nuestro ejemplo, con los trabajadores que son hilos, el núcleo sería el cerebro de cada trabajador/hilo. Un trabajador/hilo siempre tendrá una misión que hacer determinada, pero sin un cerebro/núcleo no puede hacer nada. Y aquí ya sí que nos metemos de lleno con el órgano de proceso de nuestra cabeza, que equiparamos al núcleo de un procesador; no al procesador que puede tener varios núcleos y, por tanto, varios cerebros.

Así, si repasamos el primer ejemplo de la fábrica y la tienda con un solo trabajador/hilo, con un cerebro/núcleo (Ahora sí hacemos caso a los cerebros de las imágenes), le es más que suficiente para ejecutar todo de manera correcta. Le sobraría con un procesador con un núcleo, pues más núcleos no los aprovecharía.

Generalidad de un hilo que lo hace todo ejecutándose en un núcleo - www.Jarroba.com

Por lo que un cerebro/núcleo procesará el trabajo tan rápido como pueda en el tiempo (Esta rapidez se mide con la velocidad de reloj de un núcleo de un procesador, es decir, los Herzios; cuando escuchamos que tiene, por ejemplo, 3.4GHz, esta es la velocidad que tiene cada núcleo del procesador en procesar los datos que le llegan). Vuelvo a dibujar su gráfica de tiempo con un cerebro/núcleo al lado, para representar el tiempo que tarda este en procesarlo todo.

Tiempo en ejecutarse un hilo o thread principal que tiene gran carga de trabajo en núcleo - www.Jarroba.com

El segundo ejemplo es más complejo. Estamos suponiendo que tenemos dos núcleos. Si lo volvemos a repasar, fijándonos en los cerebros/núcleos, podemos llegar a la conclusión que existen dos núcleos, uno por cada hilo que se ejecuta.

Dos hilos o threads uno en primer plano y otro en segundo plano ejecutándose cada uno en un núcleo - www.Jarroba.com

Así, con dos cerebros/núcleos se puede trabajar en paralelo como indicamos anteriormente.

Tiempo en ejecutarse dos hilos o threads, el principal y el secundario, que se reparten la carga de trabajo en dos núcleos - www.Jarroba.com

Pero también se puede dar lo siguiente con el segundo ejemplo ¿Qué ocurriría si tenemos un procesador con un único núcleo? ¿Qué hilo se ejecutaría? ¿Uno primero y otro después? ¿Dará error la aplicación? ¿Qué hará?

Dos hilos o threads uno en primer plano y otro en segundo plano con un solo núcleo - www.Jarroba.com

Es evidente que no hay suficientes cerebros/núcleos para tantos trabajadores/hilos. En algún momento uno de ellos no tendrá cerebro/núcleo y se tendrá que quedar idiota –detenido completamente- mientras el otro con cerebro/núcleo realiza sus labores. Lo razonable y justo es que compartan el cerebro/núcleo un rato cada uno para poder trabajar los dos.

Y así es como se hace. El procesador se encarga de decidir cuánto tiempo va a usar cada hilo el único núcleo que existe. Dicho de otro modo, el procesador partirá los hilos en cachos muy pequeños y los juntará salteados en una línea de tiempo. Al procesarse los dos hilos de manera salteada, y gracias a la gran velocidad con la que procesan datos los procesadores hoy día, dará la sensación de paralelismo, pero no es paralelismo. Si nos fijamos en la siguiente línea de tiempo, la longitud es tan larga como si lo hiciéramos todo en un hilo.

Tiempo en ejecutarse dos hilos o threads, el principal y el secundario, que se reparten la carga de trabajo en un único núcleo - www.Jarroba.com

No es paralelo pero sí es concurrente. La definición de concurrencia: dos o más componentes entre ellos independientes (por ejemplo, hilos con funcionalidades separadas) se ayudan para solucionar un problema común (en estos ejemplos, los hilos trabajan juntos para mostrar al usuario unos paquetes descargados de Internet). Lo concurrente se recomienda ejecutarse en paralelo para sacar el máximo partido de la concurrencia, pero como vemos no es obligatorio. Si volvemos la vista atrás, y repasamos el primer ejemplo con un solo hilo (repasar «Ejemplo sin concurrencia ni paralelismo, es decir, sin multitaréa»), comprobaremos que no existe ni la concurrencia ni el paralelismo. Y en el segundo ejemplo, con dos hilos y dos núcleos (repasar «Ejemplo de concurrencia y paralelismo, la multitaréa en todo su esplendor»), se da tanto la concurrencia como el paralelismo.

Estoy convencido de que te preguntarás ¿Si no existen varios núcleos en el procesador, entonces no tiene ninguna ventaja haber desarrollado la aplicación con hilos? Pero sí las tiene. Si lo ejecutamos todo en el hilo principal, siempre se va a bloquear al usuario. Pero si lo ejecutamos con hilos, aunque solo dispongamos de un núcleo, habrá en algún instante en el que el usuario tenga con que actuar, y el procesador ya se encargará de darle preferencia al usuario para no bloquearle.

Veamos el siguiente ejemplo en formato comic de los trabajadores con un solo cerebro/núcleo que han de compartir:

Ejemplo de concurrencia pero NO de paralelismo, es multitaréa aunque no la ideal

1) El procesador decide que empiece poseyendo al cerebro/núcleo el trabajador/hilo en segundo plano, para que descargue algún paquete de datos. Mientras, el trabajador/hilo principal no podrá hacer nada porque no tiene cerebro.

Multitaréa concurrente pero No paralelo 1 de 4 - Descarga de datos de Internet con el hilo o thread secundario que se ejecuta en el único núcleo - www.Jarroba.com

2)      El procesador ha decidido que ya lo ha tenido mucho tiempo el trabajador/núcleo en segundo plano y le pasa el turno al trabajador/hilo principal. Este, mientras pueda usar al cerebro/núcleo, colocará todos los paquetes de datos que pueda en el escaparate. Decir que el trabajador/hilo en segundo plano se ha quedado lelo y no puede hacer nada por no tener cerebro/núcleo.

Multitaréa concurrente pero No paralelo 2 de 4 - El hilo o thread principal usa el núcleo para colocar los elementos en la interfaz - www.Jarroba.com.png

3)      El procesador dice: “Turno acabado”. Le toca otra vez al trabajador/hilo secundario, que realiza su trabajo. El trabajador/hilo principal no hace nada. Y ya por lo menos el cliente/usuario dispone de algún dato.

Multitaréa concurrente pero No paralelo 3 de 4 - El hilo o thread secundario vuelve disponer del núcleo para trabajar, el usuario ya puede usar los datos - www.Jarroba.com.png

4)      Este vaivén de turnos cerebrales se repite.

Multitaréa concurrente pero No paralelo 4 de 4 - El hilo o thread principal tiene otra vez turno para usar el núcleo - www.Jarroba.com.png

Se aprecia en este ejemplo, que no es tan rápido como disponer de dos núcleos, pero tampoco tan lento como trabajar con un solo hilo que lo haga todo -en poco tiempo el usuario ya tiene algo que hacer.

En este ejemplo se ha simplificado el orden de turnos del procesador. En la mayoría de los casos, el procesador no espera a que se termine la acción completa antes de ofrecer el turno del núcleo a otro hilo. Es decir, el procesador no esperaría a que el hilo que descarga los datos, descargue el 100% del paquete entero para pasarle el núcleo al otro hilo. Sino que le puede quitar el núcleo al hilo secundario que descarga, por ejemplo, al 23,567% de la descarga. Y lo mismo con el hilo principal, podría quitarle el núcleo a mitad de la acción de colocar el paquete en el escaparate. Eso sí, al volver a tener el turno del procesador continuará desde donde se quedó. Las únicas acciones que no puede dividir el procesador son aquellas sentencias que se hacen llamar operaciones atómicas: son aquellas que no son divisibles por el procesador y se tienen que ejecutar de manera completa.

Hemos hablado de varios hilos en un solo programa, pero no de la ejecución simultánea de varios programas. Por ello lanzo una pregunta sencilla, pero que espero sea aclarativa. Si tenemos dos programas diferentes, cuyos programadores no hicieron uso de los hilos porque no sabían los que eran: un reproductor de música y un navegador de Internet ¿Qué crees que ocurriría si quiero escuchar música mientras navego por Internet si se ejecuta todo en un procesador de dos núcleos? Piénsalo un poco, porque la respuesta es que escucharía la música fluida y sin cortes, además navegaría por Internet también sin cortes. Esto se debe a que cada programa –pese a que no se usaron hilos para su desarrollo por desconocimiento- tiene por lo menos cada uno un hilo principal. Por tanto, los dos programas -que cada uno es un hilo íntegramente- puede ejecutarse sin interrupciones, cada uno en un núcleo diferente. Como hemos indicado y a modo de resumen: cada programa tiene al menos un hilo, que es el principal. Si las aplicaciones tuvieran varios hilos y se ejecutaran a la vez, tampoco pasaría nada raro; ya que el procesador y el sistema operativo se encargan de gestionar el tiempo que estarán usando cada hilo los núcleos y se produciría la multitarea.

Como programadores ¿Cómo tenemos que actuar? Tenemos que actuar como si existieran en todo momento infinitos núcleos ¿Pero la tecnología no ha llegado a tanto? Bueno, se acercará en un futuro no muy lejano. Solo hay que ver que los procesadores tienen cada vez más núcleos, y cada nuevo que sale al mercado tiene el número núcleos a la potencia de dos del anterior más nuevo (los primeros procesadores tenían 1 núcleo, luego llegaron los de 2, luego los de 4, los de 8, 16, y así hasta el infinito). También tenemos que actuar como si solo existiera nuestra aplicación en ejecución; de dividir los recursos entre las diferentes aplicaciones ya se encarga el sistema operativo.

Dicho de otro modo, da igual cuantos núcleos tenga el dispositivo del usuario, como programadores debemos programar como si hubiera todos los que necesitamos, y como si solo existiera nuestra aplicación. Al fin y al cabo no nos influye le número de núcleos ni de otras aplicaciones al programar con hilos. De esto se preocupan los que diseñan y construyen: el procesador, el sistema operativo, entre otros. Pero sí nos tiene que preocupar el usuario, quien conoce o debería conocer las características de su dispositivo, de manera tal que sabe en todo momento que aplicaciones van bien y cuales desinstalará porque van mal.

Nota sobre los dos párrafos anteriores: es la teoría utópica que es bonita, la deseable de alcanzar, pero no es real; solo la pongo para aprender y desde ella uno se tiene que dar cuenta de las limitaciones, pues depende mucho de cada sistema. Las limitaciones reales son variadas, como: que no siempre es más rápido dividir las tareas en varios hilos (dividir una tarea en muchos hilos parece que es siempre una buena idea, pero el hecho de compartir mensajes entre los hilos para que de el resultado que queramos, puede que tarde más que el tiempo ganado por la división de la tarea en hilos; es decir, fregar dos platos tardo menos haciéndolo yo, que el tiempo en explicarle a otra persona cómo fregar para que luego podamos fregar un plato cada uno a la vez; con la idea de dividir la tarea y tardar menos tiempo, el tener que comunicarme con la otra persona ha hecho que se tarde más tiempo en completar la tarea que haciéndolo yo solo), o que no podremos utilizar todos los recursos como si no existiera nada más (pues el sistema operativo nos limitará o nos limitará que haya procesos más importantes o que no queramos dejar a la máquina sin recursos), o que simplemente nos baste con tener un par de hilos aunque tengamos muchos más núcleos (si creamos una aplicación que reproduce música, con tener un solo hilo para una sola canción no sobra, pues no tendría mucho sentido que suenen varias canciones a la vez).

Puedes continuar con más detalles sobre la multitarea con explicación semejante a esta:

O practicando con:

Comparte esta entrada en:
Safe Creative #1401310112503
Multitaréa e Hilos, fácil y muchas ventajas por "www.jarroba.com" esta bajo una licencia Creative Commons
Reconocimiento-NoComercial-CompartirIgual 3.0 Unported License.
Creado a partir de la obra en www.jarroba.com

62 thoughts on “Multitaréa e Hilos, fácil y muchas ventajas”

  1. Excelente articulo muy claro y preciso.

    Me surgió una duda y no me quise quedar con ella ¿Qué pasa con los procesadores que tenían un núcleo? ¿podía uno escuchar música y navegar al mismo tiempo en internet, o se podían tener abiertos varios programas al mismo tiempo? de antemano gracias por tu respuesta, saludos

    1. Me alegra que te guste el artículo Miguel 🙂

      En relación a tu pregunta, cada programa (música, navegador de Internet, etc.) funciona en un hilo por separado (luego cada programa, que es un hilo principal, se puede dividir a su vez en más hilos). En este caso se aplicaría «Ejemplo de concurrencia pero NO de paralelismo, es multitaréa aunque no la ideal», ya que el tiempo de uso del núcleo sería dividido muy rápido entre los diferentes hilos, por lo que escucharías música y podrías navegar por Internet a la vez; posiblemente te haya pasado que cuando el ordenador tiene mucha carga la música se escucha mal, esto es porque el hilo de la música se bloquea durante ese rato ya que otro hilo se está procesando, cuando el procesador tiene poca carga no se notan los cortes de la música pues le da tiempo de sobra a procesar el hilo con la música.

      1. Si es verdad si me ha llegado a pasar que la música se llega a escuchar entrecortada o incluso en breves momentos como si estuviera escuchando un disco rayado, gracias por tomarte el tiempo para responder, Saludos.

  2. Una explicación muy interesante muy detallada y me ayudo a entender el tema demasiado, pero me queda una duda. Si la computadora tiene un solo núcleo, cómo es que ejecuta varios programas al mismo tiempo? Muchas gracias!!

    1. Buenas Maria. Me alegra que te haya gustado el artículo.

      Cada programa se ejecuta en su propio hilo principal como mínimo (todo programa al menos funciona sobre un hilo), por lo que el sistema operativo paraleliza cada programa por separado.

  3. ¡Excelente artículo!
    Muy interesante para profundizar acerca del tema, con una explicación muy detallada y ejemplos interesantes
    Tengo que admitir que disfrute de la lectura, muchas gracias por el artículo 😀

  4. Muy buena explicacion.

    A si deberian de explicar todos con ejemplos sencillos y sobre todo con los dibujitos para cada paso que se va haciendo.

    con esto no quedan dudas de esta forma pude comprender mucho mejor los hilos aunq nunca los e usado.

    Ahora con este material y otros para comenxar a entender.

    Sigue asii. con estos ejemplos sencillos pero poderosos a la hora de transmitir la información.

  5. Wow, una explicación sumamente clara para un estudiante o profesional del área.
    Muchas gracias amigo, y saludos desde Costa Rica.

  6. No suelo comentar en éste tipo de post, pero en éste es necesario.
    En realidad es increible lo bien que está explicado el tema, estuv como una hora hablandomelo a mí mismo para aprenderlo, y fue relaivamente facil gracias a los ejemplos que se muestran aqui. Saludos.

  7. Buena explicacion más claro no podria estar! Al fin me quedo en claro lo de los nucleos, hilos y multihilos.

  8. ¡No pude encontrar una mejor manera de explicación de Hilos, paralelismo y concurrencia! ¡Excelente explicacion! Gracias.

  9. Excelente articulo muy claro y entendible, muy buen recurso el de las imagenes y la informacion extra de como el procesador lidia con los hilos. Gracias Saludos

  10. Hola:

    Tengo un sistema para controlar multiples impresoras, o sea, verifica en cada impresora si está ocupada y si está libre va una DB y saca los datos para la próxima impresión. Me gustaría crea un hilo por cada impresora para que simultaneamente todas puedan ser encuestadas y conocer si necesitan que se le envie nuevas ordenes. Estoy con JAVA….que me aconsejas???….super ilustrativo tu paper…

     

    saludos

  11. Buenas, gracias por la info! tngo un programcall que ejecuta hilos, el problema es que se crean los hilos pero no los cierra y luego arroja un error que dice que java.lang.OutOfMemoryError: Unable to create new native thread 

    Sabes como se puede solucionar este problema?

    Podria crear un hilo padre que cierre los hilos hijos?

    1. Parece que creas más hilos de los soportados. Podrías pasar un puntero al hilo para poder invocar el stop pero es inseguro, podrían bloquearse.

      1. ok pero una duda más, es que mi profe dice si se hubira programado un juego que hicimos con hilos seria menos pesado y jalaria mas rapido es cierto y por que de ante mano gracias.

        1. Y que razón tiene tu profesor.

          No sé que clase de juego habrás hecho, pero supongamos que has hecho un juego de disparos, donde hay enemigos en pantalla y con una mira apuntas y disparas. Si lo hicieramos sin hilos, habría que escuchar al movimiento del usuario, dibujar en pantalla un fotograma, luego calcular cual va a ser el movimiento de la inteligencia artificial del enemigo y volver a empezar. Todos estos pasos, uno tras otro consumen tiempo, y nos quedaría un juego poco fluido (piensa que a lo mejor tarda un segundo entre fotograma y fotograma, es demasiado), donde el jugador terminaría un poco enfadado por el tiempo de respuesta.

          Sin embargo, si lo hacemos con hilos, donde se escucha al usuario en un hilo, se dibuja la pantalla en otro hilo, y se calcula el movimiento del enemigo en otro hilo; lo que es lo mismo que todo a la vez. Para el jugador todo iría muy fluido, ya que los fotogramas se producen de seguido, sus movimientos son atendidos a la par, y el cálculo de los enemigos se realiza mientras ocurre lo anterior.

          Espero que te sirva para tener una idea clara. Indicar que la creación de videojuegos es un poco más compleja que todo esto; existen IDEs (como Unity) que muchos de los hilos ya te los dan creados para que no los tengas que crear y solo te preocupes en lo que es el juego, no en la arquitectura de hilos (una buena parte).

  12. Muy bueno el post! quedo clarisimo para ser la primera vez que toco este tema! te felicito por el post! . Saludos

  13. Muy buena explicacion ,me gusta mucho la teoria de forma ilustrativa es una buena tactica para masticar el tema en varios trozos.Muchas gracias 🙂 se me despejo la mente.

  14. Excelente explicación y buenos ejemplos empleados, muchas gracias por el tiempo que empleaste, y espero que sigas asi es de gran ayuda.

  15. Hola muy buen artículo, me gustó muchoo, justo ahora estaba intentado hacer uso de varios hilos en paralelo para optimizar mi aplicación que genera gráficos ilustrativos parecido a lo que es un microsoft visio pero para android, el objetivo es poder tener una interfaz cómoda para que el usuario manipule los controles con total libertad y pueda moverlos arbitrariamente, pero lo que no ve el usuario, es el proceso (bucles de repeticion, búsquedas) que permitan determiar si al existir una figura que tenga una o varias relaciones, estas puedan manifestar su dependencia mediante el seguimiento de su nodo inmediato al provocar la accion de movimiento en la pantalla. Entonces a parti de esto, extiendo mi pregunta, es posible optimizar el uso del CPU del dispositivo al igual que la memoria, para ejecutar procesos extensos donde se realizan búsqueas y a la vez se redibuja las relaciones mediante el uso de canvas y bitmaps en los eventos de movimiento por ejemplo en onTouchEvent(Action_Move), para evitar que mi aplicación sea demasiado lenta al ejecutar lo anteriormente explicado.

    De antemano gracias por todos los comentarios contructivos que puedan facilitarme para superar este inconveniente en mi aplciación.

    Saludos

    1. Hola Fredy,
      lo que propones tiene muy buena pinta y seguramente necesites hilos para que el usuario note fluidez. Si controlas bien los hilos (AsyncTask para Android en http://jarroba.com/asynctask-en-android/) seguramente le des al usuarios suficiente fluidez y no necesites más (luego dependerá del Hadware del dispositivo del usuario). Si aún así no ves que tenga suficiente fluidez la aplicación, puedes echar un vistazo al NDK de Android (https://developer.android.com/tools/sdk/ndk/index.html), solo que aquí tendrás que controlar prácticamente todo (memoria, rendimiento, etc).

    2. Saludos Ramón muchas gracias por responder mi inquietud, sabes que este momento llevo trabajando un poco con AsyncTask pero el verdadero inconviente respecto a la fluidez de mi aplicación es en el manejo de los bitmaps, el asunto es que, como lo explique anteriormente, la mecánica de la aplicación debe permitir el seguimiento de lineas relacionales que mantengan dependencia a los nodos o figuras representadas y para esto debo dibujar constantemente los bitmaps mediante canvas acorde al movimiento de las figuras, el proceso es pesado por lo que al ejecutar esto el Garbage Collector de Android, constantemente libera memoria probocando que se pause la aplicación y la animación no se vea en tiempo real, algo que es muy molesto en realidad, el mensaje de la consola de android es el siguiente:

      11-29 22:13:01.061 1640-1640/com.example.fredy.prototipo21 D/dalvikvm﹕ GC_FOR_ALLOC freed 1562K, 44% free 4907K/8716K, paused 2ms, total 3ms
      11-29 22:13:01.071 1640-1640/com.example.fredy.prototipo21 D/dalvikvm﹕ GC_FOR_ALLOC freed 129K, 39% free 5350K/8716K, paused 4ms, total 4ms
      11-29 22:13:01.071 1640-1640/com.example.fredy.prototipo21 D/dalvikvm﹕ GC_FOR_ALLOC freed 570K, 39% free 5363K/8716K, paused 2ms, total 2ms
      11-29 22:13:01.081 1640-1640/com.example.fredy.prototipo21 D/dalvikvm﹕ GC_FOR_ALLOC freed 587K, 38% free 5410K/8716K, paused 3ms, total 3ms
      11-29 22:13:01.091 1640-1640/com.example.fredy.prototipo21 D/dalvikvm﹕ GC_FOR_ALLOC freed 633K, 38% free 5423K/8716K, paused 8ms, total 8ms

      Estuve investigando aceca de como reciclar los bitmaps para evitar que actue constantemente el garbage collector de android pero no ha sido muy util lo que he encontrado.

      Gracia de antemano por tu atención

        1. Excelente explicacion, nadie lo hubiera expresado mejor, mi pregunta es solo para reforzar lo aprendido aqui:

          Si tengo entonces dos nucleos y tengo dos procesos (una instancia del reproductor y otra del navegador) ocurre la multitarea de forma paralela (un nucleo para cada proceso principal) y concurrente, ¿que pasa si ademas el reproductor tiene 2 subprocesos mas y el navegador 3 subprocesos mas?

          ¿Para cada una de estas aplicaciones la multitarea ocurre pero no de forma ideal pues ya no existe el paralelismo pero si la concurrencia? ¿se puede decir que cada nucleo en cada aplicacion simula paralelismo?

          1. Depende de las políticas de procesamiento del procesador. Sí tendrías paralelismo en los momentos en el momento en el que dos hilos se ejecuten a la vez. Vale, tienes 3 hilos del navegador, es imposible que se ejecuten a la vez los 3 hilos del navegador en 2 núcleos; pero como es probable que se dé la combinación «AB BC CA» (siendo cada letra un hilo y cada par de letras representa el trabajo simultáneo en cada núcleo), se puede decir que se da el paralelismo.

            Aunque como bien has deducido, no es una multitaréa completa. Es más, en el peor de los casos podría no darse ni paralelismo ni si quiera concurrencia: que 1 hilo del reproductor y 1 hilo del navegador se ejecute cada uno acaparando todo el tiempo de proceso de los núcleos (es una probabilidad muy pequeña, pero existe; y por estadística se procesan millones de hilos por hora, así que lo más probable es que se de el caso eventualmente). Pero esto no lo sabremos, lo que sí sabemos es que teóricamente existe al menos un caso en el que se da el paralelismo y la multitarea (por ejemplo, el que indiqué antes), por lo que hay que deducir y decir que la multitaréa se da, pero no completa.

            El ejemplo es teórico para que se entienda la idea general, rara vez se da tan perfecto. Como has entendido, existen múltiples casos que dependerán de los hilos y las políticas del procesador. ¿Es paralelo o concurrente, es multitarea completa o incompleta? Con lo que has aprendido solo te queda deducir los posibles casos que se puedan dar 😉

  16. Muchisimas gracias.
    El tiempo y dedicación para la elaboración de éste artículo no fueron en vano.
    Saludos desde Bogotá

  17. Muchas gracias por este excelente articulo!! lo leí completamente y entendí muy bien lo de los hilos, paralelismo y concurrencia, ademas de entender de manera general como funcionan de acuerdo a los núcleos de un procesador. Muchas Gracias! me adhiero a seguirlos en todo momento por sus notas actualizadas. Se han ganado otro seguidor 😉

  18. Gracias por el articulo, a sido muy ilustrativo sobretodo para entender la diferencia entre paralelismo y concurrencia, pensaba que era todo lo mismo.
    Esperando con ansias la segunda parte.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

Uso de cookies

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies

ACEPTAR
Aviso de cookies