2008-12-10 15 views
5

Estoy escribiendo complementos de Eclipse, y con frecuencia tengo una situación en la que un trabajo en ejecución necesita detenerse por un momento, ejecutar algo de manera asincrónica en el hilo de la interfaz de usuario y reanudar.¿Cuál es la mejor manera de obtener un valor de retorno de un asyncExec en Eclipse?

Así que mi código por lo general se ve algo como:

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! 

Una forma de hacerlo es tener toda la clase de trabajo tienen algún campo. Otra es usar una clase personalizada (en lugar de anónima para esto y usar su campo de datos resultante, etc. ¿Cuál es el mejor y más elegante enfoque?

+0

Necesita aclarar la frase "ejecutar algo de forma asincrónica en el subproceso de UI" ya que el código de muestra usa display.syncExec, lo que significa que su subproceso se detendrá mientras el código en run() se ejecuta en el subproceso de visualización. – SCdF

+0

El título y el texto de la pregunta dicen 'asyncExec', pero el código de ejemplo tiene' syncExec'. ¡Dos problemas muy diferentes! ¿Cuál es? – Lii

Respuesta

4

Probablemente no debe suponer que la asincrónica Runnable habrá terminado cuando regrese la llamada asyncExec.

En cuyo caso, está tratando de enviar el resultado a oyentes/devoluciones de llamada (posiblemente patrón de comando), o si desea tener el resultado disponible más adelante en el mismo método, usando algo como java.util.concurrent.Future .

0

Bueno, si es sincronización, puede tener un valor titular de algún tipo . externo al método run()

el clásico es:

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

Dónde recipiente es sólo:

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

Esto es por supuesto hilarante y dudoso (aún más dudoso es crear una nueva lista y luego establecer y obtener el primer elemento), pero el método syncExec bloquea para que no salga nada malo.

Excepto cuando alguien regresa más tarde y hace que sea asyncExec() ..

+0

Esto no funciona para la pregunta, que es asyncExec() – jamesh

+0

El código de ejemplo en la pregunta usa syncExec() y el texto de la pregunta dice "ejecutar Job necesita pausar por un momento, ejecutar algo asincrónicamente en el hilo de la interfaz de usuario y reanudar ". Entonces creo que esto funciona –

7

creo que el contenedor por encima es la elección "correcta". También podría ser genérico para la seguridad del tipo. La elección rápida en este tipo de situaciones es la expresión idiomática final. El truco es que cualquier variable local a la que se haga referencia desde Runnable debe ser definitiva y, por lo tanto, no puede modificarse. Así que en lugar, se utiliza un único conjunto de elementos, donde la matriz es definitivo, pero el elemento de la matriz se pueden modificar:

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

Una vez más, esta es la solución "rápida" para aquellos casos donde se tiene una clase anónima y desea darle un lugar donde colocar un resultado sin definir una clase de contenedor específica.

ACTUALIZACIÓN Después pensé en esto un poco, me di cuenta de que esto funciona muy bien para el uso oyente y visitante tipo en el que la devolución de llamada se encuentra en el mismo hilo. En este caso, sin embargo, Runnable se ejecuta en un hilo diferente, por lo que no está garantizado que realmente vea el resultado después de la devolución de syncExec. La solución correcta es utilizar 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()); 

Los cambios en el valor de AtomicReference se garantiza que sea visible para todos los hilos, tal como si se tratara de declarar volátil. Esto se describe en detalle en here.

Cuestiones relacionadas