c# - tutorial - Despachador de WPF. Invocar ''colgando''
wpf presentacion (7)
Tengo una aplicación WPF algo compleja que parece "colgada" o atascada en una llamada de espera cuando intento usar el despachador para invocar una llamada en el hilo de la interfaz de usuario.
El proceso general es:
- Manejar el evento click en un botón
- Cree un nuevo hilo (STA) que: crea una nueva instancia del presentador y la IU, luego llama al método Desconectar
- Desconectar luego establece una propiedad en la interfaz de usuario llamada Nombre
- El colocador de nombre utiliza el siguiente código para establecer la propiedad:
if(this.Dispatcher.Thread != Thread.CurrentThread)
{
this.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate{
this.Name = value; // Call same setter, but on the UI thread
});
return;
}
SetValue(nameProperty, value); // I have also tried a member variable and setting the textbox.text property directly.
Mi problema es que cuando se llama al método de invocación del despachador, parece colgarse cada vez, y la pila de llamadas indica que está inactiva, espere o se una a la implementación de Invoke.
Entonces, ¿hay algo que estoy haciendo mal que me falta, obvio o no, o hay una forma mejor de llamar al hilo de la interfaz de usuario para establecer esta propiedad (y otras)?
Editar: La solución fue llamar a System.Windows.Threading.Dispatcher.Run () al final del delegado de la secuencia (por ejemplo, donde se estaba realizando el trabajo) - Gracias a todos los que ayudaron.
Esto suena como un punto muerto; esto sucedería normalmente si el hilo que llamaba .Invoke ya tenía un bloqueo / mutex / etc que el hilo de la interfaz de usuario necesita para completar su trabajo. El enfoque más simple sería usar BeginInvoke en su lugar: de esa manera, el hilo actual puede seguir funcionando, y (presumiblemente) liberará el bloqueo en breve, permitiendo que la interfaz de usuario lo adquiera. Alternativamente, si puede identificar el bloqueo ofensivo, puede liberarlo deliberadamente durante un tiempo.
La invocación es síncrona: desea Dispatcher.BeginInvoke. Además, creo que su muestra de código debe mover el "SetValue" dentro de una declaración "else".
¿Usted dice que está creando un nuevo hilo STA, se está ejecutando el despachador en este nuevo hilo?
Obtengo de "this.Dispatcher.Thread! = Thread.CurrentThread" que espera que sea un despachador diferente. Asegúrese de que se está ejecutando, de lo contrario, no procesará su cola.
Tengo un problema similar y, aunque todavía no estoy seguro de cuál es la respuesta, creo que su
if(this.Dispatcher.Thread != Thread.CurrentThread)
{
this.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate{
this.Name = value; // Call same setter, but on the UI thread
});
return;
}
debe ser reemplazado por
if(this.Dispatcher.CheckAccess())
{
this.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate{
this.Name = value; // Call same setter, but on the UI thread
});
return;
}
CheckAccess no aparecerá en Intellisense, pero está ahí y está destinado a este fin. Además, estoy de acuerdo en que en general quieres BeginInvoke aquí, sin embargo, he descubierto que no recibo actualizaciones de UI cuando hago esto de forma asíncrona. Lamentablemente, cuando lo hago de forma sincrónica obtengo una condición de punto muerto ...
Creo que quieres decir si (! This.Dispatcher.CheckAccess ())
También me estoy poniendo nervioso con Invoke, o si puedo BeginInvoke no se está llamando a mi delegado, parece que está haciendo todo por el libro :-(
Sé que este es un hilo viejo, pero aquí hay otra solución.
Acabo de arreglar un problema similar. Mi despachador estaba funcionando bien, entonces ...
Tuve que mostrar la VENTANA DEBUG -> THREAD para identificar todos los hilos que están ejecutando mi código en cualquier lugar.
Al revisar cada uno de los hilos, rápidamente vi qué hilo causó el punto muerto.
Se trataba de varios hilos que combinaban una instrucción lock (locker) { ... }
y llamaban a Dispatcher.Invoke ().
En mi caso, podría simplemente cambiar una instrucción de lock (locker) { ... }
específica, y reemplazarla por un Interlocked.Increment(ref lockCounter)
.
Eso resolvió mi problema porque se evitó el punto muerto.
void SynchronizedMethodExample() {
/* synchronize access to this method */
if (Interlocked.Increment(ref _lockCounter) != 1) { return; }
try {
...
}
finally {
_mandatoryCounter--;
}
}
Creo que esto se ve mejor con el código. Considera este escenario:
El hilo A hace esto:
lock (someObject)
{
// Do one thing.
someDispatcher.Invoke(() =>
{
// Do something else.
}
}
El hilo B hace esto:
someDispatcher.Invoke(() =>
{
lock (someObject)
{
// Do something.
}
}
Todo puede parecer fino y elegante a primera vista, pero no lo es. Esto producirá un punto muerto. Los despachadores son como colas para un hilo, y cuando se trata de puntos muertos como éstos, es importante pensar en ellos de esa manera: "¿Qué despacho anterior pudo haber atascado mi cola?". El hilo A entrará ... y se enviará bajo un candado. Pero, ¿y si el hilo B entra en el momento en que el hilo A está en el código marcado como "Haz una cosa"? Bien...
- El subproceso A tiene el bloqueo en algún objeto y está ejecutando algún código.
- El subproceso B ahora se distribuye, y el despachador tratará de obtener el bloqueo de algún objeto, bloqueando su despachador ya que el subproceso A ya tiene ese bloqueo.
- El subproceso A luego pondrá en cola otro elemento de despacho. Este elemento nunca se disparará, porque su despachador nunca terminará de procesar su solicitud anterior; ya está atorado.
Ahora tiene un hermoso punto muerto.