Un metodo robusto di monitoraggio non è riuscita lavoratori con ThreadPool
-
25-09-2019 - |
Domanda
Sto cercando un buon metodo di tracciamento (conteggio) che i lavoratori hanno fallito quando in coda con un ThreadPool e utilizzando WaitHandle.WaitAll () per tutte le discussioni alla fine.
E 'incastro un contatore di una tecnica buona o c'è una strategia più robusta?
Soluzione
Ok, ecco un approccio che si potrebbe prendere. Ho incapsulato i dati che vogliamo tenere traccia in un TrackedWorkers
di classe. C'è un costruttore su questa classe che consente di impostare il numero di lavoratori lavoreranno. Poi, gli operai vengono lanciati utilizzando LaunchWorkers
che richiede un delegato che mangia un object
e restituisce un bool
. Il object
rappresenta l'ingresso al lavoratore e il bool
rappresenta successo o il fallimento seconda true
o false
essendo il valore di ritorno, rispettivamente.
Quindi, in pratica quello che facciamo abbiamo una matrice per tenere traccia dello stato operaio. Lanciamo lavoratori e impostare lo stato corrispondente a quello dei lavoratori a seconda del valore di ritorno da parte del lavoratore. Quando i rendimenti dei lavoratori, abbiamo fissato un AutoResetEvent
e WaitHandle.WaitAll
per tutta la AutoResetEvents
da impostare.
Si noti che non v'è una classe annidata per tenere traccia del lavoro (il delegato) il lavoratore deve fare, l'ingresso che il lavoro, e di un ID
utilizzato per impostare la AutoResetEvent
status corrispondente a tale discussione.
Nota con molta attenzione che una volta che il lavoro è fatto non siamo in possesso di un riferimento al lavoro func
delegato né alla input
. Questo è importante in modo che non accidentalmente impediamo roba da essere garbage collection.
Ci sono metodi per ottenere lo stato di un determinato lavoratore, così come tutti gli indici dei lavoratori che si succedettero e tutti gli indici dei lavoratori che non è riuscita.
Un ultima nota: non considero questa produzione di codice pronto. Si tratta semplicemente di uno schizzo di un approccio che avrei preso. È necessario prendere cura di aggiungere test, gestione delle eccezioni e altri dettagli.
class TrackedWorkers {
class WorkerState {
public object Input { get; private set; }
public int ID { get; private set; }
public Func<object, bool> Func { get; private set; }
public WorkerState(Func<object, bool> func, object input, int id) {
Func = func;
Input = input;
ID = id;
}
}
AutoResetEvent[] events;
bool[] statuses;
bool _workComplete;
int _number;
public TrackedWorkers(int number) {
if (number <= 0 || number > 64) {
throw new ArgumentOutOfRangeException(
"number",
"number must be positive and at most 64"
);
}
this._number = number;
events = new AutoResetEvent[number];
statuses = new bool[number];
_workComplete = false;
}
void Initialize() {
_workComplete = false;
for (int i = 0; i < _number; i++) {
events[i] = new AutoResetEvent(false);
statuses[i] = true;
}
}
void DoWork(object state) {
WorkerState ws = (WorkerState)state;
statuses[ws.ID] = ws.Func(ws.Input);
events[ws.ID].Set();
}
public void LaunchWorkers(Func<object, bool> func, object[] inputs) {
Initialize();
for (int i = 0; i < _number; i++) {
WorkerState ws = new WorkerState(func, inputs[i], i);
ThreadPool.QueueUserWorkItem(this.DoWork, ws);
}
WaitHandle.WaitAll(events);
_workComplete = true;
}
void ThrowIfWorkIsNotDone() {
if (!_workComplete) {
throw new InvalidOperationException("work not complete");
}
}
public bool GetWorkerStatus(int i) {
ThrowIfWorkIsNotDone();
return statuses[i];
}
public IEnumerable<int> SuccessfulWorkers {
get {
return WorkersWhere(b => b);
}
}
public IEnumerable<int> FailedWorkers {
get {
return WorkersWhere(b => !b);
}
}
IEnumerable<int> WorkersWhere(Predicate<bool> predicate) {
ThrowIfWorkIsNotDone();
for (int i = 0; i < _number; i++) {
if (predicate(statuses[i])) {
yield return i;
}
}
}
}
Utilizzo di esempio:
class Program {
static Random rg = new Random();
static object lockObject = new object();
static void Main(string[] args) {
int count = 64;
Pair[] pairs = new Pair[count];
for(int i = 0; i < count; i++) {
pairs[i] = new Pair(i, 2 * i);
}
TrackedWorkers workers = new TrackedWorkers(count);
workers.LaunchWorkers(SleepAndAdd, pairs.Cast<object>().ToArray());
Console.WriteLine(
"Number successful: {0}",
workers.SuccessfulWorkers.Count()
);
Console.WriteLine(
"Number failed: {0}",
workers.FailedWorkers.Count()
);
}
static bool SleepAndAdd(object o) {
Pair pair = (Pair)o;
int timeout;
double d;
lock (lockObject) {
timeout = rg.Next(1000);
d = rg.NextDouble();
}
Thread.Sleep(timeout);
bool success = d < 0.5;
if (success) {
Console.WriteLine(pair.First + pair.Second);
}
return (success);
}
}
Il programma di cui sopra sta per lanciare sessantaquattro discussioni. Il filo i
th ha il compito di aggiungere il i
numeri e 2 * i
e stampare il risultato alla console. Tuttavia, ho aggiunto una quantità casuale di sonno (meno di un secondo) per simulare busyness e lancia una moneta per determinare il successo o il fallimento del filo. Coloro che riescono stampare la somma che avevano il compito di e true
ritorno. Coloro che non riescono nulla di stampa e false
ritorno.
Qui ho usato
struct Pair {
public int First { get; private set; }
public int Second { get; private set; }
public Pair(int first, int second) : this() {
this.First = first;
this.Second = second;
}
}