Eine robuste Methode zur Verfolgung fehlgeschlagener Arbeiter mit Threadpool
-
25-09-2019 - |
Frage
Ich suche nach einer guten Verfolgung (Zählen), die die Arbeiter in der Warteschlange mit einem Threadpool und mit WaitHandle fehlgeschlagen haben.
Ist ineinandergreifend ein Zähler eine gute Technik oder gibt es eine robustere Strategie?
Lösung
Okay, hier ist ein Ansatz, den Sie wählen könnten. Ich habe die Daten, die wir in einer Klasse verfolgen möchten, eingekapselt TrackedWorkers
. Es gibt einen Konstruktor in dieser Klasse, mit dem Sie festlegen können, wie viele Arbeitnehmer arbeiten werden. Dann werden die Arbeiter mithilfe von Mitarbeitern gestartet LaunchWorkers
was einen Delegierten erfordert, der ein frisst object
und kehrt eine zurück bool
. Das object
repräsentiert die Eingabe für den Arbeiter und die bool
repräsentiert Erfolg oder Misserfolg, je nachdem true
oder false
Der Rückgabewert ist jeweils.
Im Grunde haben wir also ein Array, um den Arbeitnehmerstaat zu verfolgen. Wir starten die Arbeiter und setzen den Status, der diesem Arbeiter entspricht, abhängig vom Rückgabewert des Arbeiters. Wenn der Arbeiter zurückkehrt, setzen wir eine AutoResetEvent
und WaitHandle.WaitAll
für alle AutoResetEvents
eingestellt werden.
Beachten Sie, dass es eine verschachtelte Klasse gibt, um die Arbeit (der Delegierte) zu verfolgen, den der Arbeiter tun soll, die Input für diese Arbeit und eine ID
Wird verwendet, um den Status festzulegen AutoResetEvent
entsprechend diesem Thread.
Beachten Sie sehr sorgfältig, dass wir nach Abschluss der Arbeit keinen Verweis auf den Arbeitsdelegierten haben func
noch zu dem input
. Dies ist wichtig, damit wir nicht versehentlich verhindern, dass Dinge Müll gesammelt werden.
Es gibt Methoden, um den Status eines bestimmten Arbeitnehmers sowie alle Indizes der Arbeitnehmer und alle Indizes der Arbeitnehmer, die fehlgeschlagen sind, zu erhalten.
Ein letzter Hinweis: Ich betrachte diese Codeproduktion nicht fürsfertig. Es ist nur eine Skizze des Ansatzes, den ich wählen würde. Sie müssen darauf achten, Tests, Ausnahmebehandlung und andere solche Details hinzuzufügen.
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;
}
}
}
}
Stichprobenverbrauch:
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);
}
}
Das obige Programm wird vierundsechzig Themen starten. Das i
Der Thread hat die Aufgabe, die Zahlen hinzuzufügen i
und 2 * i
und das Ergebnis in die Konsole drucken. Ich habe jedoch eine zufällige Menge Schlaf (weniger als eine Sekunde) hinzugefügt, um die Geschäftigkeit zu simulieren, und drehe eine Münze um, um den Erfolg oder Misserfolg des Fadens zu bestimmen. Diejenigen, die Erfolg haben, drucken die Summe, mit der sie beauftragt wurden, und kehrten zurück true
. Diejenigen, die versagen, drucken nichts und kehren zurück false
.
Hier habe ich verwendet
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;
}
}