sirve - Windows Forms Threading and Events: ListBox se actualiza rĂ¡pidamente pero la barra de progreso experimenta una gran demora
progressbar mientras se ejecuta un proceso c# (6)
Nuestro equipo está creando un nuevo sistema de flujo de trabajo de reclutamiento para reemplazar uno antiguo. Se me ha encomendado la tarea de migrar los datos antiguos al nuevo esquema. He decidido hacer esto creando un pequeño proyecto de Windows Forms ya que el esquema es radicalmente diferente y los scripts directos de TSQL no son una solución adecuada.
La clase principal sellada ''ImportController'' que hace el trabajo declara el siguiente evento de delegado:
public delegate void ImportProgressEventHandler(object sender, ImportProgressEventArgs e);
public static event ImportProgressEventHandler importProgressEvent;
La ventana principal inicia un método estático en esa clase usando un nuevo hilo:
Thread dataProcessingThread = new Thread(new ParameterizedThreadStart(ImportController.ImportData));
dataProcessingThread.Name = "Data Importer: Data Processing Thread";
dataProcessingThread.Start(settings);
los argumentos ImportProgressEvent llevan un mensaje de cadena, un valor máximo int para la barra de progreso y un valor int de progreso actual. El formulario de Windows se suscribe al evento:
ImportController.importProgressEvent += new ImportController.ImportProgressEventHandler(ImportController_importProgressEvent);
Y responde al evento de esta manera usando su propio delegado:
private delegate void TaskCompletedUIDelegate(string completedTask, int currentProgress, int progressMax);
private void ImportController_importProgressEvent(object sender, ImportProgressEventArgs e)
{
this.Invoke(new TaskCompletedUIDelegate(this.DisplayCompletedTask), e.CompletedTask, e.CurrentProgress, e.ProgressMax);
}
Finalmente, se actualizan la barra de progreso y el cuadro de lista:
private void DisplayCompletedTask(string completedTask, int currentProgress, int progressMax)
{
string[] items = completedTask.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
foreach (string item in items)
{
this.lstTasks.Items.Add(item);
}
if (currentProgress >= 0 && progressMax > 0 && currentProgress <= progressMax)
{
this.ImportProgressBar.Maximum = progressMax;
this.ImportProgressBar.Value = currentProgress;
}
}
El asunto es que el ListBox parece actualizar muy rápidamente, pero la barra de progreso nunca se mueve hasta que el lote está casi completo de todos modos? lo que da ?
No hay ganancia de threads porque sé que solo generará un hilo. El uso de un subproceso es puramente para tener una interfaz de usuario receptiva mientras SQL Server está siendo procesado con lecturas y escrituras. Ciertamente no es un hilo de corta vida.
OK, lo agradezco y me alegro de que hayas encontrado tu error, pero ¿has mirado a BackgroundWorker? Hace más o menos lo mismo que usted, pero de forma estandarizada (es decir, sin sus propios delegados) y sin la necesidad de crear un nuevo hilo, ventajas que son (quizás pequeñas, pero quizás útiles).
¿Estás seguro de que el hilo de UI se ejecuta libremente durante todo este proceso? es decir, ¿no está bloqueado en una combinación o alguna otra espera? Eso es lo que parece para mí.
La sugerencia de utilizar BackgroundWorker es buena, definitivamente superior a tratar de salir del problema con un montón de llamadas de Actualizar / Actualizar.
Y BackgroundWorker utilizará un hilo de grupo, que es una forma más amigable de comportarse que crear su propio hilo de corta duración.
¿Por casualidad ejecuta Windows Vista? He notado exactamente lo mismo en algunas aplicaciones relacionadas con el trabajo. De alguna manera, parece haber un retraso cuando la barra de progreso "anima".
@John
Gracias por los enlaces.
@Será
No hay ganancia de threads porque sé que solo generará un hilo. El uso de un subproceso es puramente para tener una interfaz de usuario receptiva mientras SQL Server está siendo procesado con lecturas y escrituras. Ciertamente no es un hilo de corta vida.
En cuanto a los martillos, tienes razón. Pero, como resultado, mi problema fue entre la pantalla y la silla, después de todo. Parece que tengo un lote de datos no usuales que tiene muchos y muchos más registros de claves externas que los otros lotes y que acaba siendo seleccionado al principio del proceso, lo que significa que el progreso actual no obtiene ++ durante unos buenos 10 segundos.
@Todas
Gracias por todas sus aportaciones, me hizo pensar, lo que me hizo buscar en otra parte del código, lo que me llevó a mi momento de humildad ahaa donde probé una vez más el error suele ser humano :)
Tal vez fuera del alcance, pero a veces es útil hacer una Application.DoEvents();
para hacer que las partes de la GUI reaccionen a la entrada del usuario, como presionar el botón cancelar en un cuadro de diálogo de la barra de estado.
Tal vez puedas probar el componente BackgroundWorker. Hace que el enhebrado sea más fácil. Ejemplos aquí: