Tengo una aplicación WinForms escrita en C# con .NET 3.5. Se ejecuta un largo proceso por lotes. Quiero que la aplicación actualice el estado de lo que está haciendo el proceso por lotes. ¿Cuál es la mejor manera de actualizar la UI?¿Cómo cedo al hilo de la interfaz de usuario para actualizar la interfaz de usuario mientras realizo el procesamiento por lotes en una aplicación de WinForm?
Respuesta
El BackgroundWorker suena como el objeto que desea.
Application.DoEvents() o posiblemente ejecute el lote en un hilo separado?
Utilice el componente backgroundworker para ejecutar el procesamiento por lotes en un subproceso separado, esto no afectará al subproceso de interfaz de usuario.
Ejecuta el largo proceso en una cadena de fondo. La clase de trabajador en segundo plano es una forma fácil de hacerlo: proporciona una compatibilidad sencilla para enviar actualizaciones de progreso y eventos de finalización para los que los manejadores de eventos son llamados en la secuencia correcta para usted. Esto mantiene el código limpio y conciso.
Para mostrar las actualizaciones, las barras de progreso o el texto de la barra de estado son dos de los enfoques más comunes.
La clave para recordar es que si usted está haciendo las cosas en un subproceso en segundo plano, debe cambiar al hilo de interfaz de usuario con el fin de actualizar los controles de ventanas, etc.
La forma más rápida y sucia está utilizando Application.DoEvents()
pero esto puede causa problemas con el orden en que se manejan los eventos. Por lo tanto, no se recomienda
El problema probablemente no es que deba ceder al hilo de la interfaz de usuario sino que haga el procesamiento en el hilo de la interfaz de usuario impidiéndole manejar los mensajes. Puede utilizar el componente backgroundworker para realizar el procesamiento por lotes en un subproceso diferente sin bloquear el subproceso de interfaz de usuario.
DoEvents() era lo que estaba buscando pero también he votado las respuestas de Backgroundworker porque parece una buena solución que investigaré un poco más.
Considere definitivamente el BackgroundWorker: cada vez que me encuentro con DoEvents(), nunca ha sido divertido solucionar problemas posteriores. –
Quiero repetir lo que mis comentaristas anteriores notaron: por favor evite DoEvents() siempre que sea posible, ya que esto es casi siempre una forma de "piratear" y causa pesadillas de mantenimiento.
Si va por la carretera BackgroundWorker (que sugiero), tendrá que ocuparse de las llamadas cruzadas a la IU si desea llamar a cualquier método o propiedad de los Controles, ya que estos son afines y deben ser ser llamado solo desde el hilo en el que fueron creados. Use Control.Invoke() y/o Control.BeginInvoke() según corresponda.
Si está ejecutando en un subproceso de fondo/trabajador, puede llamar Control.Invoke
en uno de los controles de interfaz de usuario para ejecutar un delegado en el hilo de interfaz de usuario.
Control.Invoke
es sincrónico (Espera hasta que el delegado regrese). Si no desea esperar, use .BeginInvoke()
para solo poner en cola el comando.
El valor de retorno de .BeginInvoke()
le permite verificar si el método se completó o esperar hasta que se complete.
Para aclarar lo que dice la gente sobre DoEvents, aquí hay una descripción de lo que puede suceder.
Digamos que tiene algún formulario con datos y su evento de larga ejecución lo está guardando en la base de datos o generando un informe basado en él. Comienza a guardar o generar el informe, y luego periódicamente llama a DoEvents para que la pantalla siga pintando.
Lamentablemente, la pantalla no solo está pintando, también reaccionará a las acciones del usuario. Esto se debe a que DoEvents detiene lo que está haciendo ahora para procesar todos los mensajes de Windows que esperan ser procesados por su aplicación Winforms. Estos mensajes incluyen solicitudes para volver a dibujar, así como también cualquier usuario que escriba, haga clic en, etc.
Así, por ejemplo, mientras guarda los datos, el usuario puede hacer cosas como hacer que la aplicación muestre un cuadro de diálogo modal que es completamente no relacionado con la tarea de larga ejecución (por ejemplo, Ayuda-> Acerca de). Ahora está reaccionando a las nuevas acciones de usuario dentro de la tarea que ya se está ejecutando durante mucho tiempo. DoEvents volverá cuando todos los eventos que estaban esperando cuando lo llamó hayan finalizado, y luego su tarea de ejecución larga continuará.
¿Qué pasa si el usuario no cierra el cuadro de diálogo modal? Su tarea de ejecución prolongada espera por siempre hasta que este diálogo se cierre. Si se compromete con una base de datos y realiza una transacción, ahora mantiene abierta una transacción mientras el usuario toma un café. O bien, su transacción se agota y usted pierde su trabajo de persistencia, o la transacción no expira y potencialmente puede bloquear a otros usuarios del DB.
Lo que sucede aquí es que Application.DoEvents hace que su código vuelva a ingresar. Vea la definición de wikipedia here. Tenga en cuenta algunos puntos de la parte superior del artículo, que para el código que se reentrante, es:
- No debe contener datos no constantes estáticos (o globales).
- Debe funcionar solo en los datos que le proporciona la persona que llama.
- No debe confiar en los bloqueos de los recursos singleton.
- No debe llamar a programas o rutinas de computadora no reentrantes.
Es muy poco probable que el código de larga ejecución en una aplicación Windows Forms está trabajando únicamente en datos pasados al método por el que llama, no es titular de los datos estáticos, no mantiene bloqueos, y pide solamente otros métodos de reentrada.
Como muchas personas están diciendo, DoEvents puede dar lugar a algunos escenarios muy extraños en el código. Los errores a los que puede conducir pueden ser muy difíciles de diagnosticar, y es probable que su usuario no le diga "Oh, esto podría haber sucedido porque hice clic en este botón no relacionado mientras esperaba que se guardara".
wow, ¿cómo se volvió a activar esto? ¡Creo que debería leer las fechas de publicación antes de escribir largas respuestas! Aún así, podría ayudar a alguien ... –
+1 para obtener buenos detalles. –
Backgroundworker
, y si también está intentando actualizar el hilo GUI por el manejo de la ProgressChanged
evento (como, por un ProgressBar
), asegúrese de establecer también WorkerReportsProgress=true
, o el hilo que está reportando el progreso va a morir la primera vez intenta llamar al ReportProgress
...
se emite una excepción, pero es posible que no la vea a menos que tenga activada la función 'when throwwn', y la salida solo mostrará que el hilo salió.
- 1. checkValidity para actualizar la interfaz de usuario
- 2. ¿Cómo actualizar la interfaz de usuario desde la capa empresarial?
- 3. Cómo bloquear la interfaz de usuario de Winforms mientras se ejecuta el hilo de fondo
- 4. ¿Cómo puedo actualizar la interfaz de usuario en MVVM WPF
- 5. Swing, cómo actualizar correctamente la interfaz de usuario
- 6. Cómo actualizar la interfaz de usuario de ViewModel con ObservableCollection?
- 7. ¿Por qué los elementos de la interfaz de usuario siempre se deben crear/actualizar desde el hilo de la interfaz de usuario?
- 8. Acceso a la interfaz de usuario en un hilo
- 9. Hacer interfaz de usuario para la aplicación de consola
- 10. Actualizar la interfaz de usuario de un AsyncTaskLoader
- 11. ¿Cómo puede el servicio Android actualizar la interfaz de usuario de la actividad que lo inició?
- 12. ¿Cómo puede un hilo de fondo colgar el hilo de la interfaz de usuario?
- 13. Interfaz gráfica de usuario de Python para la aplicación portátil
- 14. ¿Cómo probar una interfaz de usuario WPF?
- 15. WinForm aplicación se bloquea la interfaz de usuario durante la Operación
- 16. ¿El hilo principal es igual que el hilo de la interfaz de usuario?
- 17. IISExpress interfaz de usuario
- 18. C#: Mostrar cuadro de diálogo en el hilo de la interfaz de usuario de otro hilo
- 19. Crear un proxy INotifyPropertyChanged para enviar llamadas al hilo de la interfaz de usuario
- 20. Uso del navegador para la interfaz de usuario de escritorio
- 21. Android: ¿forma correcta de pasar un mensaje desde el hilo de fondo al hilo de la interfaz de usuario?
- 22. ¿Cómo puedo crear un 'pulso' al igual que la interfaz de usuario para una aplicación Android
- 23. Multithreading: cómo actualizar la interfaz de usuario para indicar el progreso
- 24. Interfaz de usuario para ordenar una tabla por varias columnas
- 25. SOLID y la interfaz de usuario?
- 26. Hilo de fondo contra el hilo de la interfaz de usuario
- 27. sincronización de subprocesos: sin interfaz de usuario
- 28. Interfaz de usuario completamente dirigida por teclado
- 29. ¿por qué mi servicio bloquea la interfaz de usuario?
- 30. Actualizando el hilo de la interfaz de usuario (cuadro de texto) a través de C#
Usaría BackgroundWorker y haría que el lote se ejecutara en un hilo separado. No me gusta Application.DoEvents() porque incluso si está haciendo eso, mientras no está haciendo eso y está procesando el trabajo, su UI no deja de responder por completo y puede parecer "colgada" para su usuario. –
No hagas esto, es un verdadero olor a código. Nunca he visto un 'Application.DoEvents()' en el código de producción donde pensé: "Esa fue la solución más fácil y más correcta". –
Si usa CLR Profiler 2.0, notará que el uso de DoEvents creará MUCHOS controladores, ¡que se quedan! Sugeriría usar BackgroundWorker como dijo jolson. – joek1975