Learning Machine #12 - Diseño de aplicaciones multi-hilo
Hola de nuevo a todos. En ocasiones anteriores hemos hecho aplicaciones que están compuestas por varios procesos. Pero antes de avanzar, veremos que tipo de multi-hilo es el más conveniente dependiendo de que tarea queramos realizar.En post anteriores hemos visto que hay tareas que requieren una tiempo de procesamiento largo; dependiendo del caso, hay situaciones en las que el usuario podrá seguir trabajando ya que no depende del resultado de la computación. En otros casos no es así, ya que se depende mucho de los datos resultantes.
Cuando nos encontramos en la primer situación, podemos informar o no que es lo que está sucediendo, pero cuando se presenta la segunda, por lo general es importante hacerlo. En alguna ocasión en un post, mostré cómo es que las barras de progreso suelen ser meros placebos ya que hay una serie de factores que afectan el tiempo en que cada calculo se hace (cuantas veces no hemos visto el famoso "Restan cero segundos" o "Progreso 100%" que dura más que todo lo anterior). Ahora pra entender un poco, veremos cada caso.
Caso uno: "no necesito los datos".
Ahora veremos un ejemplo, para eso modificaremos un poco el programa en C# que prueba la conjetura de Collatz. para empezar veamos el código.
using System;
using System.Windows.Forms;
using System.Numerics;
using System.Threading;
namespace ConjeturaDeCollatz
{
public partial class FrmMain : Form
{
private delegate void UpdateListDelegate(string Value);
private void UpdateList(string Value)
{
if (LBResults.InvokeRequired)
{
LBResults.Invoke(new UpdateListDelegate(UpdateList), Value);
}
else
{
LBResults.Items.Add(Value);
}
}
private void CalculateCollatz(string Number)
{
BigInteger MyNumber;
BigInteger.TryParse(Number, out MyNumber);
while (MyNumber > 1)
{
Thread.Sleep(1500);
if (MyNumber.IsEven == false)
{
MyNumber = MyNumber * 3 + 1;
UpdateList(MyNumber.ToString());
}
else
{
MyNumber = MyNumber / 2;
UpdateList(MyNumber.ToString());
}
}
}
private void RunWork(string Number)
{
Thread DoWork = new Thread(new ThreadStart(() => CalculateCollatz(Number)));
DoWork.Start();
}
private void RunWorkWithNoThread(string Number)
{
CalculateCollatz(Number);
}
public FrmMain()
{
InitializeComponent();
}
private void BtnStart_Click(object sender, EventArgs e)
{
LBResults.Items.Clear();
RunWork(TxtNumberIn.Text);
}
private void BtnStart2_Click(object sender, EventArgs e)
{
RunWorkWithNoThread(TxtNumberIn.Text);
}
}
}
Para poder hacer las pruebas, modificamos un poco el código que ya teníamos y además, agregamos un botón para que la acción se ejecute pero no en un hilo separado, si hace clic en este botón, verás que la aplicacion parecerá que se bloquea e incluso si presiona muchas veces el botón de cerrar, windows te preguntará si quieres cerrar la aplicación porque esta no responde, pero no es el caso; el proceso sigue corriendo de manera "normal", pero debido a que se encuentra la ejecución dentro del bucle "while" no puede responder a las peticiones del usuario. Por lo que simplemente podemos esperar (el usuario más bien) a que todo acabe, de cualquier forma los resultados no son necesarios.
Caso dos "necesito esos datos".
Cuando los datos son necesarios, debemos de hallar una forma de informar al usuario cuando hay datos listos para usarse, para eso vamos a crear un hilo secundario, imaginemos que queremos escalar 1000 imágenes y a cada una arreglarle el color, por lo que se debe de hacer de una por una a la vez; pero solo parte del trabajo humano porque el proceso de escalar las imágenes se puede hacer poniendo todas en una cola y escalarlas y solo cuando se ha procesado la primera, abrirla para empezar a trabajar con ella y en el fondo el escalado del resto de las imágenes puede seguir ya que nos va a tomar varios minutos en ajustar nuestra imagen a lo que necesitamos.
En alguno de los post que hemos hecho, hicimos un programa en android que se conectaba al bluetooth del Clock View (puedes ver el post), hacemos uso de dos tipos de thread, en la primera, simplemente notificábamos al usuario que se está realizando la conexión entre el dispositivo y Clock view y cuando esta se llevaba a cabo, un "thread" diferente se se ejecutaba y su función era manejar todos los datos que se transmitían desde/hacia el dispositivo y el bluetooth y mientas no se pida que la conexión se cierre (o se cierre la aplicación), este hilo seguirá corriendo en el fondo.
Y bien, por ahora es todo, el código de la conjetura de Collatz lo puedes modificar para probar el ejemplo de este post. En los siguiente post continuaremos con más programación.
Los leo luego
En alguno de los post que hemos hecho, hicimos un programa en android que se conectaba al bluetooth del Clock View (puedes ver el post), hacemos uso de dos tipos de thread, en la primera, simplemente notificábamos al usuario que se está realizando la conexión entre el dispositivo y Clock view y cuando esta se llevaba a cabo, un "thread" diferente se se ejecutaba y su función era manejar todos los datos que se transmitían desde/hacia el dispositivo y el bluetooth y mientas no se pida que la conexión se cierre (o se cierre la aplicación), este hilo seguirá corriendo en el fondo.
Y bien, por ahora es todo, el código de la conjetura de Collatz lo puedes modificar para probar el ejemplo de este post. En los siguiente post continuaremos con más programación.
Los leo luego
Vamos a programar #58 - AsyncTask en java.
Hola de nuevo a todos, el día de hoy vamos a ver cómo usar la clase "AsyncTask" para proveer de un poco de multi-threading a una aplicación.En el la aplicacion de los números de Muchhausen, vimos que empleamos una subclase llamada "UpdateList" que hereda de "AsyncTask", el nombre de la clase es bastante descriptivo, puesto que lo que hace es ejecutar una tarea de forma asíncrona.
la clase AsyncTask posee los siguientes metodos:
- doInBackground(Params... params).
- Este método se debe de sobrecargar para poder realizar una tarea de forma asíncrona.
- onCancelled(Result result)
- Se ejecuta en el hilo principal despúes de que cancel(boolean) es invocado o doInBackground(Object[]) ha terminado.
- onPostExecute(Result result).
- Se ejecuta cuando doInBackground(Params...) ha terminado su ejecución.
- OnPreExecute()
- Se ejecuta en el hilo principal antes que doInBackground(Params...).
- onProgressUpdate(Progress... values)
- Este método se ejecuta despúes de que publishProgress(Progress...) is invoked.
- publishProgress(Progress... values)
- Este método puede ser invocado desde doInBackground(Params...) para actualizar elementos en el hilo principal.
Para tener una idea de cómo funciona, retomemos el ejemplo de los números de Munchhausen. Para empezar, ¿qué es lo queremos hacer?, en este caso buscamos crear una lista de resultados en la que indiquemos si un número n cumple o no la conjetura. entonces para construir la clase AsyncTask de acuerdo a nuestros requerimientos, puede ser de la siguiente forma: private class UpdateList extends AsyncTask<Integer,Void,ArrayList> y cada parametro corresponde a:
- Integer, son los parametros que se usaran en doInBackground(), son del tipo integer porque son los números que vamos a usar cómo limites inferior y superior, y un tercero que se usará para indicar si se deben mostrar o no todos los números.
- Void, el segundo parametro cómo en eeste caso no mostramos ningún progreso en concreto, simplemente mostramos el dialogo de forma indeterminada, usualmente suele ser un valor Integer.
- ArrayList, debido a que todos los resultados de los calculos los guardamos en un "ArrayList", para poder usar esa lista cómo resultado de la ejecución, debemos de hacerlo del mismo tipo, si por alguna razón nuestro proceso debe de devolver un valor "boolean", "String" o etc. el tercer parametro debe de ser de ese tipo.
Ahora veamos el siguiente código:
// Original de https://developer.android.com/reference/android/os/AsyncTask
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return totalSize;
}
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
Esta clase se construye de la siguiente forma: DownloadFilesTask extends AsyncTask<URL, Integer, Long>, suponiendo que se llame a ejecutar con los siguientes parametros: "URL1", "URL2","URL3","URL4"..."URL10". El parámetro URL contendrá estos diez valores distintos entonces en "doInBackground(URL.. urls)" lo que se hace es primero asignar los parámetros a la variable "urls" y luego medir los elementos con "urls.length" y cada parámetro pasado al momento de iniciar la ejecución, estará en esta matriz. EL resultado de "doInBackground" es del tipo "Long" que usaremos en "onPostExecute" para mostrar al usuario cuantos bytes se descargaron. Cada vez que se ejecute "publishProgress()" en "doInBackGround()", se ejecutará "onProgressUpdate()", en esta parte es donde debemos de cambiar cualquier indicador de progreso. finalmente en "onPostExecute()", mostraremos un dialogo con la información de cuantos bytes se descargaron.
Y bien, por ahora es todo, en el siguiente post añadiremos una barra de progreso y entenderemos mejor cómo y cuando debemos de hacer uso de "AsyncTask".
![]() |
| En post anteriores ya hemos usado "AsyncTask" |
Y bien, por ahora es todo, en el siguiente post añadiremos una barra de progreso y entenderemos mejor cómo y cuando debemos de hacer uso de "AsyncTask".
Los leo luego.
Suscribirse a:
Entradas
(
Atom
)






No hay comentarios. :
Publicar un comentario