En WinForms, ¿por qué no se pueden actualizar los controles de la interfaz de usuario desde otros subprocesos?

StackOverflow https://stackoverflow.com/questions/10349

  •  08-06-2019
  •  | 
  •  

Pregunta

Estoy seguro de que hay una buena (o al menos decente) razón para ello.¿Qué es?

¿Fue útil?

Solución

Porque fácilmente puedes terminar en un punto muerto (entre otras cosas).

Por ejemplo, su subproceso secundario podría estar intentando actualizar el control de la interfaz de usuario, pero el control de la interfaz de usuario estará esperando a que se libere un recurso bloqueado por el subproceso secundario, por lo que ambos subprocesos terminan esperando que el otro termine.Como otros han comentado, esta situación no es exclusiva del código de la interfaz de usuario, sino que es particularmente común.

En otros lenguajes como C++, usted es libre de intentar hacer esto (sin que se produzca una excepción como en WinForms), pero su aplicación puede congelarse y dejar de responder si se produce un punto muerto.

Por cierto, puede decirle fácilmente al hilo de la interfaz de usuario que desea actualizar un control, simplemente cree un delegado y luego llame al método (asincrónico) BeginInvoke en ese control pasándole su delegado.P.ej.

myControl.BeginInvoke(myControl.UpdateFunction);

Esto es el equivalente a realizar un PostMessage en C++/MFC desde un subproceso de trabajo.

Otros consejos

Creo que esta es una pregunta brillante, y creo que es necesario una mejor respuesta.

Seguramente la única razón es que hay algo en un marco en algún lugar que no sea muy seguro de hilo.

Ese "algo" es casi todos los miembros de la instancia en cada control de System.Windows.Forms.

La documentación de MSDN para muchos controles en System.Windows.Forms, si no todos, dice "Cualquier miembro público estático (compartido en Visual Basic) de este tipo es seguro para subprocesos.No se garantiza que ningún miembro de la instancia sea seguro para subprocesos".

Esto significa que los miembros de la instancia como TextBox.Text {get; set;} no son reentrante.

Hacer que cada uno de esos miembros de la instancia sea seguro para subprocesos podría generar una gran cantidad de gastos generales que la mayoría de las aplicaciones no necesitan.En cambio, los diseñadores del marco .Net decidieron, y creo que correctamente, que la carga de sincronizar el acceso a los controles de formularios desde múltiples subprocesos debería recaer en el programador.

[Editar]

Aunque esta pregunta solo pregunta "por qué", aquí hay un enlace a un artículo que explica "cómo":

Cómo:Realice llamadas seguras para subprocesos a controles de Windows Forms en MSDN

http://msdn.microsoft.com/en-us/library/ms171728.aspx

Aunque parezca razonable, la respuesta de John no es correcta.De hecho, incluso cuando usas Invoke, todavía no estás seguro de no encontrarte con situaciones de bloqueo.Cuando se trata de eventos activados en un hilo en segundo plano, el uso de Invoke podría incluso provocar este problema.


La verdadera razón tiene más que ver con las condiciones de carrera y se remonta a los tiempos antiguos de Win32.No puedo explicar los detalles aquí, las palabras clave son mensajes, eventos WM_PAINT y las diferencias sutiles entre "ENVIAR" y "POST".


Puede encontrar más información aquí. aquí y aquí.

En 1.0/1.1 no se lanzaba ninguna excepción durante la depuración, lo que se obtenía en cambio era un escenario de bloqueo intermitente en tiempo de ejecución.¡Lindo!:) Por lo tanto, con 2.0 hicieron que este escenario lanzara una excepción y con razón.

La razón real de esto es probablemente (como afirma Adam Haile) algún tipo de problema de concurrencia/bloqueo.Tenga en cuenta que la API de .NET normal (como TextBox.Text = "Hello";) incluye comandos de ENVIAR (que requieren una acción inmediata), lo que puede crear problemas si se realiza en un subproceso independiente del que realiza la actualización.El uso de Invoke/BeginInvoke utiliza una POST en su lugar que pone en cola la acción.

Más información sobre ENVIAR y PUBLICAR aquí.

Es para que no tengas dos cosas intentando actualizar el control al mismo tiempo.(Esto podría suceder si la CPU cambia al otro hilo en el medio de una escritura/lectura) La misma razón por la que necesita usar mutexes (o alguna otra sincronización) al acceder a variables compartidas entre múltiples hilos.

Editar:

En otros idiomas, como C ++, eres libre de intentar hacer esto (sin una excepción que se lanza como en Winforms), ¡pero terminarás aprendiendo por las malas de la manera difícil!

Ahh, sí... cambio entre C/C++ y C# y, por lo tanto, fui un poco más genérico de lo que debería haber sido, lo siento...El tiene razón, tu poder Haz esto en C/C++, ¡pero volverá en tu contra!

También sería necesario implementar la sincronización dentro de las funciones de actualización que son sensibles a ser llamadas simultáneamente.Hacer esto para los elementos de la interfaz de usuario sería costoso tanto a nivel de aplicación como de sistema operativo, y completamente redundante para la gran mayoría del código.

Algunas API proporcionan una manera de cambiar la propiedad actual del subproceso de un sistema para que pueda actualizar temporal (o permanentemente) los sistemas desde otros subprocesos sin necesidad de recurrir a la comunicación entre subprocesos.

Hmm, no estoy muy seguro, pero creo que cuando tenemos controles de progreso como barras de espera, barras de progreso, podemos actualizar sus valores desde otro hilo y todo funciona muy bien sin ningún problema técnico.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top