Hay muchas formas de implementar hilos pero la mas sencilla es usando el componente BackgroundWorker este fue introducido desde el framework 2.0 y permite realizas operaciones costosas o duraderas en un hilo diferente al de la interfaz por lo que nuestra aplicación no se vera afectada y continuara respondiendo.
Usando este control, la gestión de hilos está encapsulada en el control de manera que el no tenemos que lidiar con hilos (threads), invokes o delegados (delegates). Que suelen hacer la vida un poco complicada.
Ahora bien se puede usar el componente BackgroundWorker arrastrándolo de la paleta:
El componente tiene 3 funciones para controlar el funcionamiento:
Evento DoWork
Este es invocado cuando se ejecuta le función RunWorkerAsync() en ese momento se genera el segundo hilo de ejecucion; y dado que no es el hilo principal pues la aplicación segira respondiendo como si nada; es decir como lo dice le nombre de la función corre asincronamente por lo que es importante que se tome en cuenta que corre de forma asincrona por lo que el codigo no esperará el aproceso para continuar:
private void button1_Click(object sender, EventArgs e) { progressBar1.Visible = true; backgroundWorker1.RunWorkerAsync(); //este se llama y se ejecuta button1.Enabled = false; //Contiua esta instruccion sin esperar el hilo button2.Enabled = false; }Es importaten señalar cuando estemos codificando el DoWork que tomemos en cuenta que el valor que regresamos queda guardado en la propiedad e.Result el cual podremos invocar despues cuando el proceso este completo; en este ejemplolos uso para regresar el resultado de una consulta ala base dedatos que llega a demorar algún tiempo, y como se ve el resultado de la consulta lo almaceno en e.Result
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { ListqueryResult = db.COMPROBANTE.Where(w => w.C_ENVIO == 0 && w.C_ENVIO != null).ToList();//&& w.CANCELA.Count < 0) var query = queryResult.Select(s => new DataBindingProjection { ID = s.C_ID, Cliente = s.RECEPTOR.R_NOMBRE, Correo = s.RECEPTOR.R_CORREO, SERIE = s.C_SERIE, FOLIO = s.C_FOLIO, FolioFiscal = s.C_FOLIOFISCAL, SubTotal = s.C_SUBTOTAL, Iva = s.C_IVA, Total = s.C_TOTAL, Saldo = decimal.Round((decimal)s.C_SALDO, 2), TIPOCAMBIO = s.C_TIPOCAMBIO, Moneda = s.C_TIPOCAMBIO == 1 ? "MXN" : "USD", FechaEmicion = s.C_FECHA, FechaVencimiento = s.C_VENCIMIETO } ).OrderBy(o=>o.SERIE).ThenBy(o=>o.FOLIO).ToList(); e.Result = query.ToList();//regresa el arreglo oque voy a desplegar }
Evento ProgressChanged
Este evento es lanzado en el hilo principal, por lo que aquí SI podemos acceder a controles del formulario de manera segura.private void backgroundWorker1_ProgressChanged(object sender,ProgressChangedEventArgs e) { progressBar1.Value = e.ProgressPercentage; //actualizamos la barra de progreso }
Evento RunWorkerCompleted
Este método se ejecuta cuando la el proceso ha concuido junto con el hilo de ejecuciony solo peude ser por 3 medio:
- El proceso termina de forma normal sin errores ni cancelaciones
- El proceso termina debido a un error durante su ejecucion
- Cancelado por el usuario/programador
En el objeto RunWorkerCompletedEventArgs nos brinda la información de como es que se completo el hilo y asi saber como trabajar con el valor de la propiedad Result,
private void backgroundWorker1_RunWorkerCompleted(object sender,RunWorkerCompletedEventArgs e) { if (e.Cancelled) { MessageBox.Show("La tarea fue cancelada"); } else if (e.Error != null) { MessageBox.Show("Setermino con error: " + (e.Error as Exception).ToString()); } else { MessageBox.Show("La busqueda a concluido "); if (e.Result != null) { dataGridView1.DataSource = e.Result; progressBar1.Visible = false; button1.Enabled = true; button2.Enabled = true; } } }
Enviado las órdenes de cancelación al control.
Por ultimo pero no menos importante como podemos cancelar el proceso una ves que ha sido lanzado? pues es facil solo se invoca la funcion CancelAsync() y el proceso terminará
private void btoCancel_Click(object sender, EventArgs e)
{
backgroundWorker1.CancelAsync();
}
En fin creo que eso es todo ojala les resulte útil is tienen dudas pregunten abajo...
He revisado varias páginas acerca de este tema y ha sido en esta donde realmente encontré la aclaración concisa a todas mis dudas. Mil felicidades.
ResponderEliminargracias si se peude ayudar en algo mas por aca estamos ;)
EliminarEste comentario ha sido eliminado por el autor.
ResponderEliminarPrimero agradecerte por el gran aporte que nos brindas sobre este tema. Seguido quería saber como puedo llenar un DataGridView con una lista de filas obtenidas de una consulta sql y que me muestra el progreso en un progressbar, he tratado me sale error: "Información adicional: Operación no válida a través de subprocesos: Se tuvo acceso al control 'dgvListadoArticulos' desde un subproceso distinto a aquel en que lo creó."
ResponderEliminarSi me puedes ayudar te dejo mi codigo. Muchas gracias!
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
_articulo = new Articulos();
dgvListadoArticulos.AutoGenerateColumns = false;
dgvListadoArticulos.DataSource = _articulo.listarArticulos();
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
pbBarra1.Value = e.ProgressPercentage;
}
private void btnEjecutarBGW_Click(object sender, EventArgs e)
{
pbBarra1.Visible = true;
backgroundWorker1.RunWorkerAsync();
btnEjecutarBGW.Enabled = false;
button2.Enabled = false;
}
Excelente tu información, estaba quemandome las pestañas con threads jajaja
ResponderEliminar