2012-05-08 14 views
5

Estoy confundido con el escenario que he encontrado con el acceso de subproceso cruzado. Esto es lo que estoy tratando de hacer:¡BeginInvoke está bloqueando la UI mientras que Invoke no lo está!

principal interfaz de usuario de hilo - elemento de menú haga clic creo un trabajador de fondo y ejecutarlo de forma asíncrona

private void actionSubMenuItem_Click(object sender, EventArgs e) 
{ 
     ToolStripMenuItem itemSelected = (ToolStripMenuItem)sender; 
     ExecuteTheActionSelected(itemSelected.Text); 
} 

El método ExecuteTheActionSelected es el siguiente:

private void ExecuteTheActionSelected(string actionSelected) 
{ 
     BackgroundWorker localBackgroundWorker = new BackgroundWorker(); 
     localBackgroundWorker.DoWork += new DoWorkEventHandler(localBackgroundWorker_DoWork); 
     localBackgroundWorker.RunWorkerAsync(SynchronizationContext.Current); 
} 

El localBackgroundWorker_DoWork tiene:

ActionExecutionHelper actionExecutioner = new ActionExecutionHelper() 
actionExecutioner.Execute(); 

El Execute método en el que la clase que tiene el método invocador, que de hecho invoca el controlador de eventos en hilo de interfaz de usuario:

public void Execute() 
{ 
     // ---- CODE ----- 
     new MethodInvoker(ReadStdOut).BeginInvoke(null, null); 
} 

protected virtual void ReadStdOut() 
{ 
     string str; 
     while ((str = executionProcess.StandardOutput.ReadLine()) != null) 
     { 
      object sender = new object(); 
      DataReceivedEventArgs e = new DataReceivedEventArgs(str); 
      outputDataReceived.Invoke(sender, e); 
      //This delegate invokes UI event handler 
     } 
} 

El controlador de eventos de interfaz de usuario es la siguiente:

private void executionProcess_OutputDataReceived(object sender, DataReceivedEventArgs e) 
{ 
    if (_dwExecuteAction != null) 
    { 
     _dwExecuteAction.ShowDataInExecutionWindow(e.Text); 
    } 
} 

Ahora aquí viene la cuestión de hilo cruzado :

public void ShowDataInExecutionWindow(string message) 
{ 
    if (rchtxtExecutionResults.InvokeRequired) 
    { 
      rchtxtExecutionResults.Invoke(new ShowDataExecutionDelegate(ShowDataInExecutionWindow), message); 
    } 
    else 
    { 
      this.rchtxtExecutionResults.AppendText(message + Environment.NewLine); 
    } 
} 

Aquí invocación no bloquea la interfaz de usuario donde como BeginInvoke bloques. Por favor, ayúdame a entender este escenario ya que estoy confundido mucho.

+0

Me temo que no sé la respuesta, pero el enfoque que tomaría para descubrirlo sería observar cómo los múltiples niveles de invocación y los trabajadores de fondo interactúan entre sí, y donde todo se ejecuta realmente de manera que entiendes cómo todo encaja. –

Respuesta

6

Sí, esto es normal. El beneficio que obtiene de Invoke() es que bloquea el subproceso de trabajo. Cuando utiliza BeginInvoke() el hilo mantiene el motor y emite solicitudes de invocación a un ritmo superior al que puede manejar el subproceso de la interfaz de usuario. Depende de lo que le pida al hilo de la interfaz de usuario que haga, pero comienza a convertirse en un problema de alrededor de 1000 invocaciones por segundo.

El subproceso de la interfaz de usuario deja de ser receptivo en este escenario, constantemente encuentra otra solicitud de invocación a la vez que bombea el bucle de mensajes y ya no realiza sus tareas habituales. Las solicitudes de entrada y pintura ya no se procesan.

La fuente clara del problema es la solicitud de invocación en cada línea de salida recuperada del proceso. Simplemente los está generando demasiado rápido. Debe solucionar esto bajando la velocidad a la que invoca. Hay una regla simple para eso, solo intentas mantener a un humano ocupado, invocando más de 25 veces por segundo gira lo que sea que produzcas, pero un borrón para el ojo. Así que guarde las líneas y mida la cantidad de tiempo que ha transcurrido desde la última invocación.

También tenga en cuenta que usar Invoke() es una solución fácil, pero no está garantizado que funcione. Es una raza, la cadena de trabajo siempre podría llamar a la siguiente invocación() poco antes de que el hilo principal vuelva a ingresar al ciclo de mensajes y lea el siguiente mensaje. En ese caso, seguirá teniendo exactamente el mismo problema.

+0

Bien, eso da una explicación sobre 'BeginInvoke()' bloqueando la UI. Pero ¿por qué el 'Invoke()' no bloquea el hilo de UI?Puedo acceder fácilmente a otros controles cuando uso 'Invoke()' –

+0

porque bloquea el hilo del trabajador para que no pueda golpear el hilo de la interfaz de usuario con solicitudes de invocación con tanta frecuencia. El punto clave es que el hilo de UI en realidad no está bloqueado, simplemente no se está ocupando de todos sus deberes normales. –

+0

¡Una pregunta más! ¿Cómo podría saber sobre cosas tan menores? :) Hombre que uno requiere más conocimiento profundo con lo que exploras. Busqué en muchos sitios, pero nunca pude llegar a tal conclusión. –

Cuestiones relacionadas