c# - squad - sincronizar hilos-sin interfaz de usuario
thread sync black squad (4)
Intento escribir código multiproceso y enfrentar algunas preguntas de sincronización. Sé que hay muchos mensajes aquí, pero no pude encontrar nada que encaje.
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:
- No tengo UI, así que no puedo hacer
beginInvoke
(o usar hilo de fondo) como suelo hacer en winforms. - Pensé implementar
ISynchronizeInvoke
en mi clase principal, pero eso parece un poco exagerado (tal vez estoy equivocado aquí). - 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 un bloqueo.
- Cada trabajo puede tomar hasta 20 minutos.
- Puedo tener hasta 20 trabajos ejecutándose simultáneamente.
Mi pregunta es:
¿Cuál es la forma correcta de notificar a mi hilo principal sobre cualquier progreso en mi hilo de trabajo?
Gracias por cualquier ayuda.
Puede notificar el hilo principal del progreso a través de 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 ese método de devolución de llamada al subproceso de trabajo cuando lo invoca (es decir, como un delegado), o el código del subproceso del trabajador puede "conocerlo" implícitamente. De cualquier manera funciona.
El jobNumber y los parámetros de estado son solo ejemplos. Es posible que desee utilizar de alguna otra forma 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 al ProgressCallback simultáneamente, de modo 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.
Si no te importa depender 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 de WindowsBase (parte de .NET 3.0 y posterior y es la base de WPF)
Si no puede confiar en .NET 3.0, diría que está en la solución correcta desde el principio: implemente la interfaz ISynchronizeInvoke en su clase principal y pásesela a la propiedad SynchronizingObject del temporizador. A continuación, se llamará a su temporizador de devolución de llamada en el hilo principal, que luego puede generar BackgroundWorkers que verifica la base de datos y ejecuta los trabajos en cola. Los trabajos reportarían 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ó este ejemplo sobre cómo implementar realmente la interfaz ISynchronizeInvoke.
También puede usar el lock
para implementar una clase JobManager
segura para JobManager
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);
}
}
}
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
es lo que usaría 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();
};