11
votes

Modèles / principes de files d'attente de fil-coffre et programme "Master / Travailleur" en Java

J'ai un problème que je crois, c'est le modèle de maître / travailleur classique, et je recherche des conseils sur la mise en œuvre. Voici ce que je pense actuellement au problème:

Il y a une "file d'attente" globale de quelque sorte, et c'est un endroit central où "le travail à faire" est conservé. Vraisemblablement, cette file d'attente sera gérée par une sorte d'objet "maître". Les fils seront générés pour aller chercher du travail à faire, et quand ils trouvent du travail à faire, ils diront que le maître (quoi que ce soit) (quoi que ce soit) "ajoutez ceci à la file d'attente du travail à faire".

Le maître, peut-être sur un intervalle, reproduire d'autres threads qui effectuent réellement le travail à faire. Une fois le fil terminé ses travaux, j'aimerais pouvoir informer le maître que le travail est terminé. Ensuite, le maître peut supprimer ce travail de la file d'attente.

J'ai fait une quantité de programmation de fil de java dans le passé, mais tout cela a été avant JDK 1.5 et, par conséquent, je ne connais pas les nouvelles API appropriées pour la manipulation de cette affaire. Je comprends que JDK7 aura Fork-Join et que cela pourrait être une solution pour moi, mais je ne suis pas en mesure d'utiliser un produit à accès précoce dans ce projet.

Les problèmes, comme je les vois, sont:

1) Comment faire en sorte que les "threads faisant le travail" communiquent à la maîtrise leur disant que leur travail est complet et que le maître peut maintenant supprimer le travail de la file d'attente

2) Comment utiliser efficacement la maîtrise que le travail ne soit que prévu une fois. Par exemple, disons que cette file d'attente comporte un million d'articles et il souhaite dire à un travailleur de "aller faire ces 100 choses". Quel est le moyen le plus efficace de garantir que lorsqu'il planifie le suiveur suivant, il obtient "les 100 prochaines choses" et non "Les 100 choses que j'ai déjà programmées"?

3) Choisir une structure de données appropriée pour la file d'attente. Ma pensée ici est que les "threads qui trouvent des travaux à faire" puissent potentiellement trouver le même travail à faire plus d'une fois, et ils enverraient un message au Maître disant "voici le travail", et le maître se rendrait compte que le travail a déjà été programmé et par conséquent devrait ignorer le message. Je veux m'assurer que je choisis la bonne structure de données de sorte que ce calcul soit aussi bon marché possible.

Traditionnellement, je l'aurais fait dans une base de données, en quelque sorte une manière finie-State-Machine, de travail "tâches" à travers le début à compléter. Cependant, dans ce problème, je ne veux pas utiliser une base de données en raison du volume élevé et de la volatilité de la file d'attente. De plus, je voudrais garder cela aussi léger que possible. Je ne veux utiliser aucun serveur d'applications si cela peut être évité.

Il est fort probable que ce problème je décrive est un problème commun avec un nom bien connu et un ensemble accepté de solutions, mais je, avec mon degré de faible non-CS, ne savez pas ce que cela s'appelle (c'est-à-dire S'il vous plaît soyez doux).

Merci pour tous les pointeurs.


1 commentaires

Vous voudrez peut-être aussi regarder lambda-the-ultimate.org/node/3521 "Fourchette Java / Framework"


6 Réponses :


4
votes

Vérifiez Java.util .concurrent dans la bibliothèque Java.

Selon votre application, il peut être aussi simple que de pénétrer ensemble de la file d'attente de blocage et de la threadpoolexecutor.

Aussi, le livre Java Concurrence dans la pratique par Brian Goetz pourrait être utile.


0 commentaires

4
votes

Tout d'abord, pourquoi voulez-vous de tenir les éléments après qu'un travailleur a commencé à les faire? Normalement, vous auriez une file d'attente de travail et un travailleur prend des éléments de cette file d'attente. Cela permettrait également de résoudre le « comment puis-je empêcher les travailleurs d'obtenir le même article » -problem.

Pour vos questions:

1) comment avoir les « fils font la travail » communiquer au maître en leur disant que leur travail est complète et que le maître peut maintenant supprimer le travail de la file d'attente

Le maître pouvait écouter les travailleurs qui utilisent les auditeur / motif d'observateur

2) comment avoir efficacement le maître garantie que le travail est que jamais prévu une fois. Par exemple, supposons que cette file d'attente a un million d'articles, et veut dire un travailleur à « aller faire ces 100 choses ». Quel est le plus efficace manière de garantir que lorsqu'elle les horaires de travail à l'autre travailleur, il obtient « les 100 choses » et non « Les 100 choses que je l'ai déjà prévue "?

Voir ci-dessus. Je laisserais les travailleurs tirent les éléments de la file d'attente.

3) le choix d'une données appropriées Structure pour la file d'attente. ma pensée voici que les « fils à trouver du travail faire » pourrait trouver le même travail à faire plus d'une fois, et ils avaient envoyer un message à l'adage maître « Le travail est ici », et le maître se rendre compte que le travail a déjà été prévu et devrait par conséquent ignorer le message. Je veux assurer que je choisis la structure de données à droite de telle sorte que ce calcul est aussi pas cher que possible.

Il y a un file d'attente de blocage depuis Java 5


3 commentaires

Merci à tous pour la réponse. Tim, à votre première question, qui est une bonne question: je crois que je dois garder des objets sur la file d'attente, car le "threads de travail sortant et la recherche de travail à faire" doit savoir quel travail a déjà été programmé. Pour un exemple concret, imaginez un programme qui doit sortir et trouver des "anciens fichiers à déplacer". Les threads les trouvent, ajoutez-les à la file d'attente. Mais sur les exécutions ultérieures, si ces fichiers n'ont pas encore été déplacés, les threads "Finder" trouveront les mêmes fichiers. Avoir un sens? Des moyens plus appropriés de traiter ce problème? Merci encore.


Peut-être que vous n'avez pas besoin de déranger à ce sujet. Il y a une bonne qualité sur les systèmes asynchrones - Idempotence. Le système doit être protégé contre le traitement du double message (parlant en mathématiques F (x) devrait être égal à F (f (x)), de sorte que l'état du système ne change pas si un message traité deux fois). Votre exemple est un bon exemple d'idempotence dans le système. Nous pourrions passer un message sur un fichier particulier deux fois au travailleur et rien de mal n'arrive. Si le fichier a déjà déménagé, nous sautons simplement une tâche.


Vous pouvez définir une file d'attente de travail et à côté de cette liste de travail. Lorsqu'un fil de travail prend un article de la file d'attente, vous l'ajoutez à la liste des travaux. Lorsque le travailleur est terminé, vous pouvez le supprimer de la liste dans le travail. Si un article est soumis en tant que nouveau, vous pouvez vérifier si c'est déjà dans la file d'attente ou dans la liste d'ignorer.



0
votes

Si vous êtes ouvert à l'idée du printemps, vérifiez votre projet d'intégration de printemps. Cela vous donne toute la bouteille de file d'attente / fil-piscine hors de la boîte et vous permet de vous concentrer sur la logique commerciale. La configuration est conservée au minimum à l'aide de @Annotations.

BTW, le goetz est très bon.


0 commentaires

7
votes

Autant que je comprends vos exigences, vous avez besoin exécutorservice . ExecuTeurservice a

submit(Callable task)


2 commentaires

Pour ajouter à ce que @Dotsid suggère, je voudrais souligner que cette bibliothèque standard fait beaucoup, sinon tout, la OP demander et il est simple d'utiliser et cela fonctionne. Vous pouvez accumuler jusqu'à 100 ans ou des milliers de tâches sans trop d'effort.


Merci à tous pour les réponses réfléchies. Je ne sais pas si c'est la réponse "canonique", mais à la fin, après avoir lu le livre Goetz, ce que j'ai fini avec beaucoup de choses comme cette réponse.



1
votes

N'oubliez pas Jini et JavaSpaces. Ce que vous décrivez des sons de manière très similaire au modèle de producteur / consommateur classique que les architectures spatiales excellent à.

Un producteur écrira les travaux dans l'espace. 1 ou plusieurs consommateurs subiront des emplois (sous une transaction) et travailleront à ce sujet en parallèle, puis écrivez les résultats. Comme il est sous une transaction, si un problème survient, le travail est mis à la disposition d'un autre consommateur.

Vous pouvez évoluer cela trivialement en ajoutant plus de consommateurs. Cela fonctionne particulièrement bien lorsque les consommateurs sont des ordinateurs virtuels distincts et vous évoluez sur le réseau.


0 commentaires

0
votes

Cela ne ressemble pas à un problème de maîtrise-travailleur, mais un client spécialisé au-dessus d'une threadpool. Étant donné que vous avez beaucoup de threads de nettoyage et pas de nombreuses unités de traitement, cela peut être utile simplement faire une passe de pontage, puis une passe informatique. En stockant les éléments de travail dans un ensemble, la contrainte d'unicité éliminera les doublons. La deuxième passe peut soumettre tous les travaux à un service exécutorservice pour effectuer le processus en parallèle.

Un modèle de maître-travailleur suppose généralement que le fournisseur de données a tout le travail et le fournit au maître de gérer. Le maître contrôle l'exécution des travaux et traite un calcul distribué, des délais, des défaillances, des tentatives, etc. Une abstraction de jointure Fork-Join est un fournisseur de données récursif plutôt que itératif. Une abstraction de la carte de carte est un travailleur maître multi-étapes utile dans certains scénarios.

Un bon exemple de maître-travailleur est pour des problèmes de manière triviale parallèle, tels que la recherche de nombres premiers. Une autre est une charge de données dans laquelle chaque entrée est indépendante (validation, transformation, étape). La nécessité de traiter un ensemble de travail connu, gérer les défaillances, etc. est ce qui rend un modèle de maître-travailleur différent d'un bassin de fil. C'est pourquoi un maître doit être contrôlé et pousse les unités de travail, alors qu'un threadpool permet aux travailleurs de tirer le travail d'une file d'attente partagée.


0 commentaires