2010-03-17 13 views
6

Estoy intentando escribir código multithreading y enfrentar algunas preguntas de sincronización. Sé que hay muchos mensajes aquí, pero no pude encontrar nada que encaje.sincronización de subprocesos: sin interfaz de usuario

Tengo un System.Timers.Timer que transcurre cada 30 segundos hasta el DB y comprueba si hay nuevos trabajos. Si encuentra uno, ejecuta el trabajo en el hilo actual (el temporizador abre un nuevo hilo por cada tiempo transcurrido). Mientras el trabajo se está ejecutando, necesito notificar al hilo principal (donde está el cronómetro) sobre el progreso.

Notas:

  1. no tengo la interfaz de usuario, así que no puedo hacer beginInvoke (o usar hilo de fondo) como suelo hacer en WinForms.
  2. Pensé implementar ISynchronizeInvoke en mi clase principal, pero eso parece un poco exagerado (tal vez estoy equivocado aquí).
  3. Tengo un evento en mi clase de trabajo y la clase principal se registra e invoco el evento cada vez que lo necesito, pero me preocupa que pueda causar el bloqueo.
  4. Cada trabajo puede tomar hasta 20 minutos.
  5. Puedo tener hasta 20 trabajos ejecutándose simultáneamente.

Mi pregunta es:

¿Cuál es la manera correcta de notificar a mi hilo conductor sobre el avance en mi hilo de trabajo?

Gracias por cualquier ayuda.

+0

No se puede notificar a un hilo. Puedes notificar un objeto. Describe mejor esa parte y obtendrás mejores respuestas. –

Respuesta

1

Usa eventos. La clase BackgroundWorker, por ejemplo, está diseñada específicamente para lo que tienes en mente.

http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx

La función ReportProgress junto con el evento ProgressChanged son la que se uso para actualizaciones de progreso.

pullJobTimer.Elapsed += (sender,e) => 
{ 
    BackgroundWorker worker = new BackgroundWorker(); 
    worker.WorkerReportsProgress = true; 
    worker.DoWork += (s,e) => 
    { 
     // Whatever tasks you want to do 
     // worker.ReportProgress(percentComplete); 
    }; 
    worker.ProgressChanged += mainThread.ProgressChangedEventHandler; 
    worker.RunWorkerAsync(); 
}; 
+0

Siento que está mal usar BackgroundWorker y puedo explicar por qué. System.Timers.Timer pullJobTimer = new System.Timers.Timer (INTERVALO); pullJobTimer.Elapsed + = new System.Timers.ElapsedEventHandler (PullQJobs); Puedo abrir un nuevo BackgroundWorker en pulljobs que es verdadero y luego tendré ProgressChanged. Pero el temporizador transcurrido abre un nuevo hilo y luego voy a abrir un nuevo hilo de Backgroundworker ??? se siente mal, ¿no? O tal vez no entendí tu idea. – UshaP

+0

Ni siquiera tiene que abrir un nuevo hilo. BackgroundWorker lo hace por usted cuando lo ejecuta de forma asincrónica a través de 'RunWorkerAsync()'. Esto tampoco presenta una pérdida de memoria, como Jim Mischel sugirió, porque la suscripción al evento se destruye con la instancia de BackgroundWorker cuando se recolecta basura. – Jake

+0

Sé que el backgroundworker abre un hilo pero también el temporizador. Así que el Temporizador de uso de la clase principal -> El temporizador abre un nuevo hilo por cada transcurrido -> luego ejecuto backgroundworker que abre otro hilo. Y eso es lo que me hace sentir incómodo. – UshaP

1

Puede notificar el hilo principal del progreso mediante un método de devolución de llamada. Es decir:

// in the main thread 
public void ProgressCallback(int jobNumber, int status) 
{ 
    // handle notification 
} 

Puede pasar que el método de devolución de llamada para el subproceso de trabajo cuando se invoca (es decir, como un delegado), o el código del subproceso de trabajo puede "saber" sobre implícitamente. De cualquier manera funciona.

Los parámetros jobNumber y status son solo ejemplos. Es posible que desee utilizar de alguna otra manera para identificar los trabajos que se están ejecutando, y es posible que desee utilizar un tipo enumerado para el estado. Independientemente de cómo lo haga, tenga en cuenta que varios subprocesos invocarán ProgressCallback simultáneamente, por lo que si está actualizando cualquier estructura de datos compartida o escribiendo información de registro, deberá proteger esos recursos con bloqueos u otras técnicas de sincronización.

También puede usar eventos para esto, pero mantener las suscripciones de eventos del hilo principal actualizadas puede ser un problema potencial. También tiene la posibilidad de una pérdida de memoria si olvida anular la suscripción del hilo principal de los eventos de un hilo de trabajo en particular. Aunque los eventos ciertamente funcionarían, recomendaría la devolución de llamada para esta aplicación.

+0

Jim, creo que es una llamada de sincronización mientras necesito una llamada asincrónica – UshaP

+0

La devolución de llamada de progreso se invoca en el hilo de trabajo, no en el hilo principal. Entonces es una llamada asincrónica desde la perspectiva del hilo principal. –

1

Si no te importa dependiendo de .NET 3.0 puedes usar Dispatcher para ordenar las solicitudes entre subprocesos.Se comporta de forma similar a Control.Invoke() en Windows Forms pero no tiene la dependencia de Formularios. Sin embargo, necesitará agregar una referencia al ensamblado WindowsBase (parte de .NET 3.0 y posterior y es la base de WPF)

Si no puede depender de .NET 3.0 entonces yo diría que usted en la solución correcta desde el principio: implemente la interfaz ISynchronizeInvoke en su clase principal y páselo a la propiedad SynchronizingObject del temporizador. A continuación, se llamará a su temporizador de devolución de llamada en el hilo principal, que puede generar BackgroundWorkers que verifica la base de datos y ejecuta los trabajos en cola. Los trabajos informarán el progreso a través del evento ProgressChanged que coordinará la llamada al hilo principal automáticamente.

Una búsqueda rápida en Google reveló this example sobre cómo implementar realmente la interfaz ISynchronizeInvoke.

2

También puede usar lock para implementar una clase JobManager segura para subprocesos que rastrea el progreso sobre los diferentes subprocesos de trabajo. En este ejemplo, solo mantengo el conteo de los hilos de los trabajadores activos, pero esto se puede extender a las necesidades de informes de progreso.

class JobManager 
{ 
    private object synchObject = new object(); 

    private int _ActiveJobCount; 

    public int ActiveJobsCount 
    { 
     get { lock (this.synchObject) { return _ActiveJobCount; } } 
     set { lock (this.synchObject) { _ActiveJobCount = value; } } 
    } 

    public void Start(Action job) 
    { 
     var timer = new System.Timers.Timer(1000); 

     timer.Elapsed += (sender, e) => 
     { 
      this.ActiveJobsCount++; 
      job(); 
      this.ActiveJobsCount--; 
     }; 

     timer.Start(); 
    } 
} 

Ejemplo:

class Program 
{ 
    public static void Main(string[] args) 
    { 
     var manager = new JobManager(); 

     manager.Start(() => Thread.Sleep(3500)); 

     while (true) 
     { 
      Console.WriteLine(manager.ActiveJobsCount); 

      Thread.Sleep(250); 
     } 
    } 
} 
Cuestiones relacionadas