2008-11-14 4 views
9

J'ai écrit un programme qui compte les lignes, les mots et les caractères dans un texte: il le fait avec des threads. Ça marche parfois bien, mais pas très bien d'autres fois. Ce qui finit par arriver, c'est que les variables pointant vers le nombre de mots et de caractères comptés sont parfois courtes et parfois non.quand un thread est-il hors de portée?

Il me semble que les threads se terminent parfois avant de pouvoir compter tous les mots ou caractères qu'ils veulent. Est-ce parce que ces threads sortent de la portée quand la boucle while (true) se casse?

J'ai inclus le code de la partie filiforme de mon problème ci-dessous:

private void countText() { 
    try { 
    reader = new BufferedReader(new FileReader("this.txt")); 
    while (true) { 
     final String line = reader.readLine(); 
     if(line == null) {break;} 
     lines++; 
     new Thread(new Runnable() {public void run() {chars += characterCounter(line);}}).start(); 
     new Thread(new Runnable() {public void run() {words += wordCounter(line);}}).start(); 
     println(line); 
    } 

    } catch(IOException ex) {return;} 

} 

(Sous Question:. Ceci est la première fois que je l'ai demandé quelque chose et le code posté je ne veux pas utiliser StackOverflow à la place de google et de wikipedia et je m'inquiète que ce n'est pas une question appropriée? J'ai essayé de rendre la question plus générale afin que je ne demande pas seulement de l'aide avec mon code ... mais, y at-il un autre site Web où ce genre de question pourrait être plus approprié?)

Répondre

7

Une conception filetée différente faciliterait la recherche et la résolution de ce type de problème, et serait plus efficace par-dessus le marché.C'est une réponse longue, mais le résumé est "si vous faites des discussions en Java, consultez java.util.concurrent dès que humainement possible)".

Je suppose que vous êtes en train de multithreader ce code pour apprendre des threads plutôt que d'accélérer le comptage des mots, mais c'est un moyen très inefficace d'utiliser des threads. Vous créez deux threads par ligne - deux mille threads pour un fichier de mille lignes. La création d'un thread (dans les JVM modernes) utilise les ressources du système d'exploitation et est généralement assez coûteuse. Lorsque deux (et encore moins deux mille) threads doivent accéder à une ressource partagée (tels que vos compteurs chars et words), la contention de mémoire qui en résulte nuit également aux performances.

Faire les variables compteur synchronized comme Chris Kimpton suggests ou Atomic comme WMR suggests va probablement corriger le code, mais il fera aussi bien pire l'effet de discorde. Je suis sûr qu'il ira plus lentement qu'un algorithme à un seul thread. Je suggère d'avoir un seul thread de longue vie qui s'occupe chars, et un pour words, chacun avec une file d'attente de travail à laquelle vous soumettez des travaux chaque fois que vous voulez ajouter un nouveau numéro. De cette façon, un seul thread écrit dans chaque variable, et si vous apportez des modifications au design, il sera plus évident de savoir qui est responsable de quoi. Ce sera également plus rapide car il n'y a pas de conflit de mémoire et vous ne créez pas des centaines de threads dans une boucle serrée.

Il est également important, une fois que vous avez lu toutes les lignes dans le fichier, à attente pour tous les fils pour terminer avant l'impression sur les valeurs des compteurs, sinon vous perdez les mises à jour de threads qui havre de paix n'est pas encore fini. Avec votre design actuel, vous devez créer une grande liste de threads que vous avez créés et les parcourir à la fin pour vérifier qu'ils sont tous morts. Avec une conception de file d'attente et de threads de travail, vous pouvez simplement dire à chaque thread de vider sa file d'attente et d'attendre que cela soit fait. Java (à partir de la version 1.5) rend ce type de conception très facile à implémenter: consultez la section java.util.concurrent.Executors.newSingleThreadExecutor. Il est également facile d'ajouter plus de concurence plus tard (en supposant un verrouillage correct, etc.), car vous pouvez simplement passer à un pool de threads plutôt qu'à un seul thread.

+0

Je n'attendais pas la fin des discussions. Vous avez raison, je fais juste cela pour connaître les méthodes que j'utiliserai avec les threads: l'affectation ne nécessitait pas du tout de threads. Comment attendez-vous qu'un fil finisse? Pourrais-je juste attendre Thread.activeCount() pour retourner un petit nombre? – Ziggy

+0

Thread.join() attend la mort d'un seul thread. Attendre que le nombre de threads soit égal à 1 pourrait fonctionner - je suppose que vous pourriez rencontrer des conditions de course avec des threads en cours de démarrage, mais je ne suis pas sûr. –

+0

Si vous voulez obtenir le fil des discussions, je vous recommande de regarder dans la façon d'exécuter les tâches de l'Executor/pool de threads/file d'attente. Une fois que vous avez compris, il est beaucoup plus facile de raisonner que de créer des threads manuellement. –

3

Cela me semble une bonne question ... Je pense que le problème pourrait être lié à l'atomicité y des caractères + = et des mots + = - plusieurs threads peuvent appeler cela en même temps - faites-vous quelque chose pour vous assurer qu'il n'y a pas d'entrelacement.

C'est:

Discussion 1, a des caractères = 10, veut ajouter 5

Discussion 2, a des caractères = 10, veut ajouter 3

Discussion 1 fonctionne nouveau total, 15

discussion 2 fonctionne nouveau total, 13

discussion 1 à 15 ensembles caractères

Thread 2 définit les caractères à 13.

Cela peut être possible, sauf si vous utilisez synchronisé lors de la mise à jour de ces variables.

+1

Aha! Vous voyez, j'ai totalement appris sur l'entrelacement et l'atomicité et synchronisé et verrouillé, mais cela ne m'aurait jamais traversé l'esprit. C'est exactement le problème, sans aucun doute! – Ziggy

+1

Hmm ... J'ai utilisé synchronized (this) {autour du + = stuff} mais je reçois toujours des résultats imprévisibles ... – Ziggy

+0

oh mec, je ne pense pas que ce soit ça. J'ai ajouté un println (Thread.activeCount()); cela me donnerait une idée de ce qui se passait. Il semble que je n'ai parfois que 12 threads actifs avant la fin de la boucle while. C'est le problème: pas assez de temps! – Ziggy

4

Comme Chris Kimpton déjà fait remarquer correctement, vous avez un problème avec la mise à jour de chars et words dans différents threads. La synchronisation sur this ne fonctionnera pas non plus car this est une référence au thread actuel, ce qui signifie que différents threads se synchroniseront sur différents objets. Vous pouvez utiliser un « objet de verrouillage » supplémentaire, vous pouvez synchroniser sur, mais la meilleure façon de résoudre ce problème serait probablement utiliser AtomicIntegers pour les 2 compteurs:

AtomicInteger chars = new AtomicInteger(); 
... 
new Thread(new Runnable() {public void run() { chars.addAndGet(characterCounter(line));}}).start(); 
... 

Bien que cela va probablement résoudre votre problème, Sam Stoke's more detailed answer est tout à fait raison , le design original est très inefficace. Pour répondre à votre question à propos de la sortie d'un thread: Vous démarrez deux nouveaux threads pour chaque ligne de votre fichier et ils s'exécuteront tous jusqu'à la fin de leur méthode run(). C'est à moins que vous ne les fassiez daemon threads), dans ce cas ils vont quitter dès que les threads de démon sont les seuls à fonctionner dans cette JVM.

+0

J'ai implémenté AtomicIntegers, ce qui a augmenté mon taux de réussite. Il y a toujours des courses dans lesquelles les deux comptes sont inférieurs à ce qu'ils devraient être ... – Ziggy

+0

Vous n'attendez probablement pas que toutes les discussions soient terminées avant d'imprimer le résultat. Voir ma réponse ci-dessous. –

Questions connexes