2010-01-19 23 views
6

Tengo un proyecto de mascota en el que estoy trabajando que tiene varios hilos de trabajo. Resulta difícil seguir enviando todo a la consola, por lo que quiero desarrollar una UI que tenga un área de salida por hilo. Quiero saber la mejor manera para que los hilos envíen actualizaciones a la IU. Tengo dos ideas:Actualizar la IU desde múltiples hilos de trabajo (.NET)

1) Haga que cada subproceso establezca un indicador "DataUpdated" cuando haya nuevos datos disponibles y haga que la UI compruebe periódicamente si hay nuevos datos.

2) Cree cada subproceso con una devolución de llamada a un método de actualización de interfaz de usuario (...) para llamar cuando haya nuevos datos disponibles.

Actualmente estoy inclinado hacia (2) por dos razones: No me gusta la idea de "verificar" cada hilo, y porque esta es mi primera aplicación multiproceso y (2) parece más simple de lo que probablemente sea. Quiero saber:

  • ¿Qué opción es preferible en términos de simplicidad y eficiencia?
  • ¿Tiene algún consejo para implementar (2) o algo parecido (es decir, más orientado a eventos)?

Respuesta

12

Se puede implementar fácilmente (2) mediante la creación de componentes BackgroundWorker y hacer el trabajo en sus manipuladores DoWork:

BackgroundWorker bw = new BackgroundWorker(); 
bw.WorkerReportsProgress = true; 
bw.DoWork += /* your background work here */; 
bw.ProgressChanged += /* your UI update method here */; 
bw.RunWorkerAsync(); 

Cada BackgroundWorker puede informar sobre el progreso al hilo de interfaz de usuario llamando ReportProgress: aunque esto está diseñado principalmente para informar el progreso en un proceso limitado, eso no es obligatorio; también puede pasar sus propios datos personalizados si eso es lo que su actualización de UI requiere. Llamarías a ReportProgress desde tu controlador DoWork.

Lo bueno de BackgroundWorker es que se encarga de un montón de detalles cruzados para usted. También se ajusta al modelo de actualizaciones impulsado por eventos que usted (con razón) prefiere a las devoluciones de llamada explícitas.

+2

excelente ... gracias! Ahora solo tengo que esperar para dejar mi trabajo diario para poder hacer algo de programación real. – iandisme

+0

Haga esto en el marco compacto (3.5). – Royal

1

Yo voto por el # 2 también pero con BackgroundWorkers en lugar de System.Threading.Threads.

+0

Y la función de devolución de llamada tiene que ser seguro para subprocesos ... – Oded

0

Puede hacer que sus subprocesos de trabajo generen eventos y que el subproceso principal de la interfaz de usuario agregue controladores de eventos. Debe tener cuidado de no generar demasiados eventos, ya que podría ponerse peor si sus subprocesos de trabajo generan varios eventos por segundo.

This artículo ofrece una visión general.

0

La forma preferida de implementar multiprocesamiento en su aplicación es usar el componente BackgroundWorker. El componente BackgroundWorker usa un modelo basado en eventos para multihilo. El subproceso de trabajador ejecuta su controlador de eventos DoWork y el subproceso que crea los controles ejecuta los controladores de evento ProgressChanged y RunWorkerCompleted.
Cuando actualiza los controles de su UI en el controlador de eventos ProgressChanged, estos se actualizan automáticamente en el hilo principal, lo que evitará que obtenga excepciones de cruce cruzado.

Mire here para ver un ejemplo sobre cómo usar el backgroundworker.

1

En la mayoría de los casos, lo más fácil sería usar el componente BackgroundWorker como se sugiere en la respuesta de itowlson, y si es posible, le sugiero que use ese enfoque. Si, por alguna razón, no puede utilizar un componente de BackgroundWorker para su propósito, como si estuviera desarrollando con .Net 1.1 (¡ay!) o con un marco compacto, entonces es posible que necesite utilizar un enfoque alternativo:

Con los controles de Winform, debe evitar modificar los controles en cualquier hilo que no sea el hilo que creó originalmente el control. El componente BackgroundWorker maneja esto para usted, pero si no lo está utilizando, puede y debe usar la propiedad InvokeRequired y el método Invoke que se encuentran en la clase System.Windows.Forms.Control. A continuación se muestra un ejemplo que utiliza esta propiedad y método:

public partial class MultithreadingForm : Form 
{ 
    public MultithreadingForm() 
    { 
     InitializeComponent(); 
    } 

    // a simple button event handler that starts a worker thread 
    private void btnDoWork_Click(object sender, EventArgs e) 
    { 
     Thread t = new Thread(WorkerMethod); 
     t.Start(); 
    } 

    private void ReportProgress(string message) 
    { 
     // check whether or not the current thread is the main UI thread 
     // if not, InvokeRequired will be true 
     if (this.InvokeRequired) 
     { 
      // create a delegate pointing back to this same function 
      // the Invoke method will cause the delegate to be invoked on the main UI thread 
      this.Invoke(new Action<string>(ReportProgress), message); 
     } 
     else 
     { 
      // txtOutput is a UI control, therefore it must be updated by the main UI thread 
      if (string.IsNullOrEmpty(this.txtOutput.Text)) 
       this.txtOutput.Text = message; 
      else 
       this.txtOutput.Text += "\r\n" + message; 
     } 
    } 

    // a generic method that does work and reports progress 
    private void WorkerMethod() 
    { 
     // step 1 
     // ... 
     ReportProgress("Step 1 completed"); 

     // step 2 
     // ... 
     ReportProgress("Step 2 completed"); 

     // step 3 
     // ... 
     ReportProgress("Step 3 completed"); 
    } 
} 
0

Si va a crear sus propios hilos (no BackgroundWorker o hilos ThreadPool) se puede especificar un método de devolución de llamada desde el hilo principal que se llama desde el subproceso de trabajo. Esto también le permite pasar argumentos a la devolución de llamada e incluso devolver un valor (como un indicador de ir/no seguir). En su devolución de llamada se actualiza la interfaz de usuario a través del despachador de control de destino:

public void UpdateUI(object arg) 
{ 
    controlToUpdate.Dispatcher.BeginInvoke(
     System.Windows.Threading.DispatcherPriority.Normal 
     , new System.Windows.Threading.DispatcherOperationCallback(delegate 
     { 
      controToUpdate.property = arg; 
      return null; 
     }), null); 
    } 
} 
Cuestiones relacionadas