Timer periodico condizionale usando maniglie di attesa
-
13-11-2019 - |
Domanda
Ho bisogno di un timer equivalente che eseguirà periodicamente alcune azioni specifiche (ad esempio aggiornando alcuni progressi nel database o controllano nuovi lavori da eseguire in un database).
Queste azioni sono legate a un waithandle che specifica se il lavoro deve essere eseguito o meno. Quindi fondamentalmente questo potrebbe essere, ad esempio, un autoresetevent che è impostato dall'esterno quando c'è una nuova entità in un database e attiva la ricerca di queste nuove entità. Il timer è necessario perché voglio limitare il numero di query al database. Quindi, se arrivano 10 nuove notifiche, AutomaticResevent verrà impostata solo una volta e ci sarà anche una sola domanda per tutti questi.
I miei primi tentativi sembrano questo:
class ConditionalScheduler
{
public void AddConditionalAction(WaitHandle handle, Action action)
{
lock (syncRoot)
{
handles.Add(handle);
actions.Add(handle, action);
}
}
private readonly object syncRoot = new object();
private readonly Dictionary<WaitHandle, Action> actions = new Dictionary<WaitHandle, Action>();
private readonly List<WaitHandle> handles = new List<WaitHandle>();
public void RunTimerLoop()
{
do
{
WaitHandle[] waitHandles = handles.ToArray();
int index = WaitHandle.WaitAny(waitHandles);
if (index >= 0)
{
WaitHandle handle = waitHandles[index];
Action action;
lock (syncRoot)
{
action = actions[handle];
}
action();
}
Thread.Sleep(5000);
} while (true);
}
Il problema con questo approccio è che WaitHandle.Waitany mi darà solo l'indice del primo waithandle che viene attivato. Quindi, se ho 2 o più waithandles che vengono attivati, allora eseguirò solo le prime azioni e non le altre.
Hai un design migliore per ottenere i risultati richiesti per l'esecuzione di tutte le azioni attivate nel periodo di tempo specificato? Se semplifica la questione, potrei usare un altro tipo di meccanismo di notifica anziché i waithandles.
Grazie!
Soluzione
Forse potresti andare con una coda a due thread / esecuzione sfusa? Nota che il codice Down è solo un concetto che sto scrivendo senza Visual Studio, quindi sto facendo in un elenco, questo è stato meglio essere fatto con una coda ma non ricordo la sintassi senza intellisense :)
private readonly Dictionary<WaitHandle, Action> actions = new Dictionary<WaitHandle, Action>();
private readonly List<WaitHandle> handles = new List<WaitHandle>();
private readonly List<WaitHandle> triggeredHandles = new List<WaitHandle>();
// thread 1
public void CheckLoop()
{
do
{
WaitHandle[] waitHandles = handles.ToArray();
int index = WaitHandle.WaitAny(waitHandles);
if (index >= 0)
{
lock (triggeredHandles)
{
triggeredHandles.Add(waitHandles[index]);
}
}
} while (true);
}
// thread 2
public void RunLoop()
{
do
{
lock(triggeredHandles)
{
foreach (var th in triggeredHandles) {
Action action;
lock (syncRoot)
{
action = actions[th];
}
action();
}
triggeredHandles.Clear();
}
Thread.Sleep(5000);
} while (true);
}
Altri suggerimenti
Hai preso in considerazione l'uso delle classi di timer incorporate? System.Threading.Timer
è un'implementazione leggera che utilizza il ThreadPool
, o System.Timers.Timer
che fornisce eventi correlati e più funzionalità.
Probabilmente ti risparmierai un sacco di problemi usando Estensioni reattive (RX) - Ha molti ottimi modi per comporre eventi nel modo in cui stai richiedendo.
Questo sito è un modo utile per vedere il tipo di cose che Rx può fare.