eclipse - titles - pierce the heaven elsword
¿Cuál es la mejor manera de obtener un valor de retorno de un asyncExec en Eclipse? (3)
Bueno, si es sincronización, puede tener un valor sostenedor 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());
Donde el contenedor es justo:
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 (incluso 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 vuelve más tarde y lo convierte en asyncExec()
..
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.
Entonces mi código generalmente se ve algo así 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 hacer que toda la clase de trabajo tenga 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 enfoque mejor y más elegante?
Probablemente no deberías estar asumiendo que el Runnable
asincrónico habrá terminado antes de que asyncExec
llamada a asyncExec
.
En ese caso, está buscando 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
. java.util.concurrent.Future
.
Creo que el contenedor de arriba es la opció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. Por lo tanto, en su lugar, utiliza una única matriz de elementos, donde la matriz es definitiva, pero el elemento de la matriz se puede 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]);
De nuevo, esta es la solución "rápida" para aquellos casos en los que tiene una clase anónima y desea darle un lugar para incluir un resultado sin definir una clase de contenedor específica.
ACTUALIZACIÓN Después de pensar en esto un poco, me di cuenta de que esto funciona bien para el uso del tipo de oyente y visitante, donde la devolución de llamada está 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 usar una 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 están garantizados para ser visibles por todos los hilos, al igual que si se declarara volátil. Esto se describe en detalle aquí .