2010-12-07 23 views
2

Dans le programme client sur lequel je travaille, nous distribuons des appels de serveur à différents threads, pour ne pas verrouiller l'interface utilisateur (l'équivalent d'un SwingWorker).Quelle est la bonne façon de suivre la trace de la pile d'origine dans un nouveau Thread?

Ceci est réalisé par une classe de modèle héritant d'une classe abstraite contenant la méthode « mise à jour », qui prépare le nouveau thread et exécuter le code à partir d'une méthode abstraite dans cette discussion nouvellement créé (ainsi que d'autres tweaks)

Cela fonctionne correctement, mais mon problème est que lors du débogage (ou de la journalisation), il est difficile de garder une trace de la méthode appelée "update", car la trace de la pile se termine par la création du nouveau Thread.

Quelle serait la bonne façon de suivre la trace de la pile qui a conduit à appeler ce nouveau fil? Idéalement, d'une manière qui apparaîtrait dans le navigateur de pile du débogueur (à partir d'Eclipse dans ce cas, l'idée est de naviguer facilement dans le contexte initial).

Répondre

2

Un bon moyen de stocker efficacement une pile empilée consiste simplement à construire une exception. Plus tard, si vous voulez inspecter l'appel stacktrace exception.getStackTrace() qui fera le travail lent de la résolution des cadres de la pile aux méthodes. Par conséquent, vous pouvez créer un new Exception sur la construction de votre thread de travail ou le transmettre au thread de travail. Notez que vous devrez obtenir eclipse pour évaluer exception.getStackTrace(), car l'objet d'exception n'aura pas les détails avant vous. Par ailleurs, vous devriez probablement utiliser un ExecutorService pour gérer le cycle de vie de vos threads.

Modifier Je suggère cette façon parce qu'il aura un impact minimal sur les performances dans le cas habituel où vous ne voulez pas voir la trace de la pile.

+0

Le stockage de la trace de pile (ou dans ce cas une exception) serait en effet une idée. Malheureusement, il ne donnerait l'information que dans les "variables", pas dans l'arbre de la pile d'Eclipse. Mais je suppose que le fait d'avoir l'air là serait trop compliqué/hacky. – Gnoupi

2

Habituellement, les traces de pile ne traversent pas les filetages, donc cela va être difficile. Cependant, dans le constructeur de votre classe de travailleur, vous pouvez accéder au thread en cours avec ...

Thread current = Thread.currentThread 

Vous pouvez alors obtenir la trace de la pile actuelle de ce thread en appelant ...

StackTraceElement[] currentStack = current.getStackTrace(); 

Vous pouvez ensuite stocker cela dans une variable d'instance pour votre travailleur et voir cela à partir de votre débogueur. Cela doit être fait avant que le contrôle passe dans le nouveau thread, c'est pourquoi j'ai suggéré de le faire dans le constructeur. Cependant, toute méthode qui est appelée avant la méthode start() du nouveau thread fonctionnera correctement.

+0

Proche de la solution donnée par daveb, avec la différence qu'il ajouterait l'appel réel à "getStackTrace", et serait peut-être un peu plus lourd pour les cas où un tel débogage n'est pas requis. Merci pour les détails, cependant. – Gnoupi

1

Vous dites que vous faites "comme SwingWorker". Dans ce cas, pourquoi ne pas utiliser un ExecutorService? Java framework concurrent est assez bien fait, et vous permettrait d'éviter les pièges classiques du filetage, parmi eux votre question.

+0

Le "framework" avec ces appels filetés a été fait avant (sans) la connaissance du paquet concurrent, donc c'est pourquoi il est fait "à la main", d'une certaine façon. Il y a beaucoup de choses autour, donc changer n'est pas quelque chose de trivial. Je suis cependant intéressé à savoir ce que cela pourrait améliorer. Cela me permettrait-il de savoir quelle méthode s'appelle ma "callable", si j'ai un point d'arrêt dans le traitement appelé? – Gnoupi

0

J'ai juste eu le même problème - plusieurs points dans le système envoyaient un courrier à travers la même logique enveloppée dans un ExecutorService.

Tout comme @daveb je crée un vide Exception et passe dans le Runnable:

Executors.newSingleThreadExecutor().submit(new Mailer(..., new Exception())); 

maintenant à l'intérieur de Mailer je l'instance Exception à utiliser dans les expressions de journalisation:

public Mailer(..., final Exception originalStackKeeper) { 
    ... 
    this.originalStackKeeper = originalStackKeeper; 
} 

... 
LOG.error("There was an error while sending mail, originating here: ", 
    originalStackKeeper); 

Mais encore mieux: chaque fois que j'attrape une exception dans Mailer, je peux le faire:

} catch (final SomeException e) { 
    LOG.error("There was an error while sending mail.", 
     originalStackKeeper.initCause(e)); 
} 

Donc je dis à l'original Exception: utilisons votre pile: voici la raison pour laquelle nous vous utilisons, et ici nous vous connectons. Il me semble que ce n'est pas vraiment un hack, mais plutôt une façon propre d'utiliser le mécanisme "cause" dans les exceptions Java.