Bon, voici une approche que vous pourriez prendre. J'ai encapsulé les données que nous voulons suivre dans une classe TrackedWorkers
. Il existe un constructeur sur cette classe qui vous permet de définir combien de travailleurs vont travailler. Ensuite, les travailleurs sont lancés en utilisant LaunchWorkers
, ce qui nécessite un délégué qui mange un object
et renvoie un bool
. Le object
représente l'entrée pour le travailleur et le bool
représente le succès ou l'échec en fonction de true
ou false
étant la valeur de retour, respectivement.
Donc, fondamentalement, ce que nous faisons, nous avons un tableau pour suivre l'état des travailleurs. Nous lançons les travailleurs et définissons le statut correspondant à ce travailleur en fonction de la valeur de retour du travailleur. Lorsque le travailleur revient, nous définissons un AutoResetEvent
et un WaitHandle.WaitAll
pour tous les AutoResetEvents
à définir. Notez qu'il existe une classe imbriquée pour suivre le travail (le délégué) que le worker doit effectuer, l'entrée de ce travail et une ID
utilisée pour définir le statut AutoResetEvent
correspondant à ce thread.
Notez très soigneusement qu'une fois le travail terminé, nous ne détenons pas de référence au délégué de travail func
ni au input
. Ceci est important afin que nous n'empêchions pas accidentellement des choses d'être collectées.
Il existe des méthodes permettant d'obtenir le statut d'un worker particulier, ainsi que tous les index des workers qui ont réussi et tous les index des workers qui ont échoué.
Une dernière remarque: Je ne considère pas que cette production de code soit prête. Ce n'est qu'une esquisse de l'approche que je prendrais. Vous devez prendre soin d'ajouter des tests, la gestion des exceptions et d'autres détails.
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;
}
}
}
}
utilisation de l'échantillon:
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);
}
}
Le programme ci-dessus va lancer soixante-quatre fils. Le i
th thread a pour tâche d'ajouter les numéros i
et 2 * i
et d'imprimer le résultat à la console. Cependant, j'ai ajouté une quantité aléatoire de sommeil (moins d'une seconde) pour simuler l'activité et je retourne une pièce de monnaie pour déterminer le succès ou l'échec du fil. Ceux qui réussissent impriment la somme à laquelle ils ont été assignés et renvoient true
. Ceux qui échouent n'impriment rien et retournent false
.
Ici, je l'ai utilisé
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;
}
}
Comment envisagez-vous les fils de rapports leur statut? Serait-ce correct s'ils se contentent de mettre un 'bool' à' true' ou 'false'? – jason
Je pensais que la méthode de mise en file d'attente aurait un compteur statique –
Je ne suis pas; il semble qu'un compteur ne soit utile que pour vous dire combien de travailleurs ont réussi ou échoué, mais pas quels travailleurs ont réussi ou échoué. Qu'est-ce que je rate? – jason