c# - Dispatcher Invoke(…) vs BeginInvoke(…) confusión
multithreading (1)
Cuando usa Dispatcher.BeginInvoke
significa que programa la acción dada para su ejecución en el subproceso de la interfaz de usuario en un momento posterior, y luego retorna el control para permitir que el subproceso actual continúe ejecutándose. Invoke
bloquea a la persona que llama hasta que finaliza la acción programada.
Cuando use BeginInvoke
su bucle se ejecutará súper rápido ya que BeginInvoke
regresa de inmediato. Esto significa que está agregando muchas y muchas acciones a la cola de mensajes. Los estás agregando mucho más rápido de lo que realmente pueden procesarse. Esto significa que hay mucho tiempo entre cuando se programa un mensaje y cuando realmente se puede ejecutar.
La acción real que está ejecutando utiliza el campo _number
. Pero _number
está siendo modificado por el otro hilo muy rápidamente y mientras la acción está en la cola . Esto significa que no mostrará el valor de _number
en el momento en que programó la acción, sino más bien qué es después de que haya continuado en su ciclo muy cerrado.
Si utiliza Dispatcher.Invoke
cambio, evita que el bucle se "adelante" y que tenga múltiples eventos programados, lo que garantiza que el valor que está escribiendo sea siempre el valor "actual". Además, al forzar cada iteración del bucle a esperar a que se ejecute el mensaje, hace que el bucle sea mucho menos "ajustado", por lo que no puede ejecutarse tan rápidamente en general.
Si desea utilizar BeginInvoke
lo primero que realmente necesita hacer es ralentizar su ciclo. Si desea que actualice el texto cada segundo, o cada 10 ms, o lo que sea, puede usar Thread.Sleep
para esperar el tiempo adecuado.
A continuación, debe tomar una copia de _number
antes de pasarla al Dispatcher
para que muestre el valor en el momento en que lo programó, no en el momento en que se ejecuta:
while (true)
{
if (_number++ > 10000)
_number = 0;
int copy = _number;
this.Dispatcher.BeginInvoke(new Action(() => UpdateText(copy))
, System.Windows.Threading.DispatcherPriority.Background, null);
Thread.Sleep(200);
}
private void UpdateText(int number)
{
this.Text = number.ToString();
}
Estoy confundido por qué no puedo hacer que esta aplicación de contador de prueba funcione con 2 (o más) cuadros de contratexto que se ejecutan simultáneamente con el uso de "BeginInvoke" en mi Dispatcher en el método Count ().
Puede resolver el problema sustituyendo el BeginInvoke por un Invoke. Pero esto no resuelve mi confusión.
Aquí está el código de ejemplo del que estoy hablando:
public class CounterTextBox : TextBox
{
private int _number;
public void Start()
{
(new Action(Count)).BeginInvoke(null, null);
}
private void Count()
{
while (true)
{
if (_number++ > 10000) _number = 0;
this.Dispatcher.BeginInvoke(new Action(UpdateText), System.Windows.Threading.DispatcherPriority.Background, null);
}
}
private void UpdateText()
{
this.Text = "" + _number;
}
}