2008-12-10 11 views
5

J'écris des plugins Eclipse, et j'ai souvent une situation où un Job en cours d'exécution doit faire une pause pendant un court moment, exécuter quelque chose de manière asynchrone sur le thread de l'interface utilisateur, et reprendre.Quelle est la meilleure façon d'obtenir une valeur de retour d'un asyncExec dans Eclipse?

Donc, mon code ressemble généralement à:

Display display = Display.getDefault(); 
display.syncExec(new Runnable() { 
    public void run() { 
       // Do some calculation 
       // How do I return a value from here? 
    } 
}); 
// I want to be able to use the calculation result here! 

Une façon de le faire est d'avoir toute la classe d'emploi ont un champ. Un autre est d'utiliser une classe personnalisée (plutôt que anonyme pour cela et utiliser son champ de données résultant, etc Quelle est la meilleure et la plus élégante approche?

+0

Vous devez clarifier l'instruction "exécuter quelque chose de manière asynchrone sur le thread UI" car votre code utilise clairement display.syncExec, ce qui signifie que le thread de votre travail s'arrêtera pendant que le code de run() est exécuté. – SCdF

+0

Le titre et le texte de la question indiquent 'asyncExec', mais l'exemple de code a' syncExec'. Ce deux problèmes très différents! Laquelle est-ce? – Lii

Répondre

4

Vous ne devriez probablement pas supposer que le Runnable asynchrone aura fini au moment où l'appel asyncExec revient. Dans ce cas, vous cherchez à pousser le résultat dans listeners/callbacks (peut-être un motif de commande), ou si vous voulez avoir le résultat disponible plus tard dans la même méthode, en utilisant quelque chose comme java.util.concurrent.Future .

0

Eh bien, si c'est sync vous pouvez simplement avoir un détenteur de valeur quelconque . externe à la méthode run()

le classique est:

final Container container = new Container(); 
Display display = Display.getDefault(); 
display.syncExec(new Runnable() 
{ 
    public void run() 
    { 
    container.setValue("foo"); 
    } 
} 
System.out.println(container.getValue()); 

Où conteneur est juste:

public class Container { 
    private Object value; 
    public Object getValue() { 
    return value; 
    } 
    public void setValue(Object o) { 
    value = o; 
    } 
} 

Ceci est bien sûr hilarant et douteux (encore plus douteux est la création d'une nouvelle liste, puis la définition et l'obtention du 1er élément), mais la méthode syncExec bloque donc rien de mal en vient.

Sauf quand quelqu'un revient plus tard et fait asyncExec() ..

+0

Cela ne fonctionne pas pour la question, qui est asyncExec() – jamesh

+0

L'exemple de code dans la question utilise syncExec() et le texte de la question indique "Running Job doit mettre en pause pendant un court moment, exécuter quelque chose de manière asynchrone sur le thread UI et reprenez ". Donc je pense que cela fonctionne. –

7

Je pense que le récipient au-dessus est le choix « bonne ». Il pourrait également être générique pour la sécurité de type. Le choix rapide dans ce genre de situation est l'idiome final du tableau. L'astuce est que toutes les variables locales référencées à partir du Runnable doivent être finales, et ne peuvent donc pas être modifiées. Ainsi, au lieu, vous utilisez un seul ensemble d'éléments, où le tableau est définitif, mais l'élément du tableau peut être modifié:

final Object[] result = new Object[1]; 
Display display = Display.getDefault(); 
display.syncExec(new Runnable() 
{ 
    public void run() 
    { 
    result[0] = "foo"; 
    } 
} 
System.out.println(result[0]); 

Encore une fois, cela est la solution « rapide » pour les cas où vous avez Une classe anonyme et vous voulez lui donner une place pour coller un résultat sans définir une classe Container spécifique.

MISE À JOUR Après avoir pensé à cela un peu, je réalise cela fonctionne très bien pour l'utilisation du type d'écouteur et visiteur où le rappel est dans le même thread. Dans ce cas, cependant, le Runnable s'exécute dans un thread différent, donc vous n'êtes pas sûr de voir le résultat après le retour de syncExec. La bonne solution est d'utiliser un AtomicReference:

final AtomicReference<Object> result = new AtomicReference<Object>(); 
Display display = Display.getDefault(); 
display.syncExec(new Runnable() 
{ 
    public void run() 
    { 
    result.set("foo"); 
    } 
} 
System.out.println(result.get()); 

Les modifications apportées à la valeur de AtomicReference sont garantis visible par tous les threads, comme si elle était déclarée volatile. Ceci est décrit en détail here.