2008-11-13 6 views
17

De vez en cuando mi GUI de aplicaciones deja de redibujar. Hay muchos hilos que activan todo tipo de eventos (como temporizadores o datos de red listos, etc.). También hay muchos controles que se suscriben a estos eventos. Por eso, todos los manejadores de eventos juegan el juego InvokeRequired/Invoke. Ahora descubrí que cuando la GUI se congela, muchos subprocesos esperan que Invoke() vuelva. Parece que el mensaje bomba dejó de bombear. Los manipuladores de este aspecto:Invoke() bloquea

private void MyEventHandler(object sender, EventArgs e) { 
    if (InvokeRequired) { 
     Invoke(new EventHandler(MyEventHandler), sender, e); 
     return; 
    } 

    SetSomeStateVariable(); 
    Invalidate(); 
} 

¿Alguna idea?

Solución: BeginInvoke(). Parece que siempre debe usar BeginInvoke() si tiene muchos CrossThread-Events ...

Gracias.

Gracias a todos.

EDITAR: Parece BeginInvoke() realmente lo resolvió. No congelar hasta ahora.

+0

¿Quién se dirige cuando se congela? ¿Es un control o evento particular, o es aleatorio? – Will

+0

Realmente no entiendo lo que quieres decir. – EricSchaefer

+0

Sus eventos se organizan en el hilo de la interfaz de usuario de a uno por vez. Alguien está bloqueando durante este proceso. Me pregunto si es el mismo método que se organiza cada vez que se bloquea, y/o si se está actualizando el mismo control que bloques cada vez. – Will

Respuesta

26

Invoke espera hasta que el evento se maneje en el hilo de la GUI. Si desea que sea un uso asincrónico, BeginInvoke()

+2

Lo sé. BeginInvoke probablemente solo escondería el problema. Todos los invocadores bloquean indefinidamente. – EricSchaefer

+1

EricSchaefer, te estás perdiendo el punto. Invoke espera hasta que se complete el evento que llamó. Sin embargo, la invocación solo se ejecutará una vez que la función actual se haya ejecutado. Estás estancado. BeginInvoke no esperará a que se ejecute la función llamada, por lo tanto, no se interrumpe. –

+0

Tal vez me falta algo más que un solo punto. El evento que llama a MyEventHandler es activado por otro hilo (por ejemplo, uno que espera datos de la red), no un evento UI. También se congela solo "de vez en cuando". – EricSchaefer

6

¿Interbloqueo quizás? ¿Se asegura de que los eventos nunca se disparen mientras se mantiene un bloqueo?

¿Puedes ver esto con un depurador adjunto? Si es así, hágalo congelar y luego presione el botón "pausa" - y vea qué está haciendo el hilo de la interfaz de usuario.

Tenga en cuenta que si puede salirse con la suya con BeginInvoke en lugar de Invoke, la vida es un poco más fácil ya que no se bloqueará.

También tenga en cuenta que no es necesario el bit "nuevo manejador de sucesos" - sólo

Invoke((EventHandler) MyEventHandler, sender, e); 

debería estar bien.

+0

Bueno, lo hice, pero ¿cómo identifico el hilo de UI? – EricSchaefer

+0

Solo desde la memoria aquí: creo que deberías llamar a Invoke() en el control apropiado. El control entonces debería ejecutar el método en el hilo de la interfaz gráfica de usuario. Por favor dime si estoy equivocado. – lowglider

+0

@lowglider: eso no era lo que estaba preguntando ... – EricSchaefer

0

La respuesta más probable (interbloqueo) ya se ha sugerido.

Otra forma de simular este comportamiento es reducir el número de subprocesos de agrupación y los puertos de terminación de E/S; ¿No ha llamado ThreadPool.SetMaxThreads(...) por casualidad?

+0

No. (necesita al menos 10 caracteres para comentarios ...) – EricSchaefer

3

Al ver esta pregunta, puedo ver que no obtendrá ninguna respuesta que solucione el problema de inmediato, ya que la mayoría de ellos requieren que se resuelva el problema, y ​​ocurre con poca frecuencia que esto es casi imposible . Entonces, déjame sugerirte que hagas algunos cambios de código que podrían ayudarte a identificar al culpable en el campo.

Te sugiero que crees una clase estática cuyo único propósito es manejar todas tus llamadas de Invoke. Sugeriría que esta clase tiene un método que toma un Control, (para llamar a invocar) una Acción (el método que se invocará) y una descripción (que contiene la información que necesitaría saber para identificar el método y lo que es va a hacer).

Dentro del cuerpo de este método, le sugiero que coloque esta información (método, descripción) y la devuelva inmediatamente.

La cola debe ser atendida por un solo hilo, que separa el par de acción/mensaje de la cola, registra la hora actual y la descripción de la Acción en un par de propiedades, y luego invoca() la Acción.Cuando la acción vuelve, la descripción y la hora se borran (su DateTime puede ser anulable, o establecerlo en DateTime.Max). Tenga en cuenta que, dado que todos los invocadores se agrupan de uno en uno en el hilo de la interfaz de usuario, aquí no se pierde nada al actualizar la cola.

Ahora, aquí es donde llegamos al punto de esto. Nuestra clase de invocación debe tener un latido System.Threading.Timer thread. ¡Este NO debería ser un objeto windows.forms.timer, ya que se ejecuta en el subproceso UI (y se bloquearía cuando el ui está bloqueado!).

El trabajo de este temporizador es mirar periódicamente en el momento en que se invoca la acción actual. Si DateTime.Now - BeginTime> X, el temporizador de latido decidirá que esta Acción ha bloqueado. El temporizador de latido registrará (independientemente de cómo se registre) la DESCRIPCIÓN registrada para esa Acción. Ahora tiene una grabación de lo que sucedía en el momento en que su UI se bloqueó y puede depurarlo mejor.

Sé que no es una respuesta a su problema, pero al menos al hacer esto usted puede hacerse una idea de lo que está sucediendo en el momento en que está bloqueado.

+0

Buenas ideas. Tendré que tocar los 55 (!) Invoca de todos modos. – EricSchaefer

+0

La congelación no ocurrió, cuando hice lo que sugeriste. Así que cambié todos los Invoke() a BeginInvoke() s. – EricSchaefer