7

我正在编写 Eclipse 插件,并且经常遇到正在运行的 Job 需要暂停片刻,在 UI 线程上异步运行某些东西,然后恢复的情况。

所以我的代码通常看起来像:

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!

一种方法是让整个 Job 类都有一些字段。另一个是使用自定义类(而不是匿名的,并使用其结果数据字段等。最好和最优雅的方法是什么?

4

3 回答 3

8

我认为上面的 Container 是“正确”的选择。它也可以被泛化以实现类型安全。在这种情况下的快速选择是最终的数组习语。诀窍是从 Runnable 引用的任何局部变量都必须是最终的,因此不能修改。因此,您可以使用单元素数组,其中数组是最终的,但可以修改数组的元素:

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

同样,对于那些您有一个匿名类并且您希望在不定义特定 Container 类的情况下给它一个放置结果的地方的情况,这是“快速”的解决方案。

更新 在我考虑了一下之后,我意识到这对于回调在同一个线程中的侦听器和访问者类型的使用效果很好。但是,在这种情况下,Runnable 在不同的线程中执行,因此不能保证您在 syncExec 返回后实际看到结果。正确的解决方案是使用 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());

保证对 AtomicReference 值的更改对所有线程都是可见的,就像它被声明为 volatile 一样。这在此处进行了详细描述。

于 2008-12-10T04:34:58.120 回答
4

您可能不应该假设异步Runnable将在asyncExec调用返回时完成。

在这种情况下,您正在考虑将结果推送到侦听器/回调中(可能是命令模式),或者如果您确实希望稍后在相同的方法中使用结果,使用类似java.util.concurrent.Future.

于 2008-12-10T14:17:06.150 回答
0

好吧,如果它是同步的,那么您可以在该方法之外拥有某种类型的值持有者run()

经典的是:

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

容器只是:

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

这当然是搞笑和狡猾的(更狡猾的是创建一个新的列表,然后设置和获取第一个元素)但是syncExec方法阻塞所以没有什么不好的。

除非有人稍后回来并成功asyncExec()..

于 2008-12-10T03:55:50.150 回答