Domanda

Le scrivo plugin Eclipse, e spesso hanno una situazione in cui ha bisogno di un lavoro in esecuzione per mettere in pausa per un breve periodo, qualcosa di eseguire in modo asincrono sul thread dell'interfaccia utente, e riprendo.

Quindi, il mio codice di solito sembra qualcosa di simile:

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!

Un modo per farlo è quello di avere l'intera classe di lavoro hanno qualche campo. Un altro è quello di utilizzare una classe personalizzata (anziché anonimo per questo e usare il suo conseguente campo di dati, etc. Qual è l'approccio migliore e più elegante?

È stato utile?

Soluzione

You probably shouldn't be assuming that the async Runnable will have finished by the time the asyncExec call returns.

In which case, you're looking at pushing the result out into listeners/callbacks (possibly Command pattern), or if you do want to have the result available at a later in the same method, using something like a java.util.concurrent.Future.

Altri suggerimenti

I think the Container above is the "right" choice. It could be also be genericized for type safety. The quick choice in this kind of situation is the final array idiom. The trick is that a any local variables referenced from the Runnable must be final, and thus can't be modified. So instead, you use a single element array, where the array is final, but the element of the array can be modified:

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]);

Again, this is the "quick" solution for those cases where you have an anonymous class and you want to give it a place to stick a result without defining a specific Container class.

UPDATE After I thought about this a bit, I realized this works fine for listener and visitor type usage where the callback is in the same thread. In this case, however, the Runnable executes in a different thread so you're not guaranteed to actually see the result after syncExec returns. The correct solution is to use an 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());

Changes to the value of AtomicReference are guaranteed to be visible by all threads, just as if it were declared volatile. This is described in detail here.

Well, if it's sync you can just have a value holder of some kind external to the run() method.

The classic is:

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

Where container is just:

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

This is of course hilarious and dodgy (even more dodgy is creating a new List and then setting and getting the 1st element) but the syncExec method blocks so nothing bad comes of it.

Except when someone comes back later and makes it asyncExec()..

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top