Каков наилучший способ получить возвращаемое значение из asyncExec в Eclipse? - PullRequest
6 голосов
/ 10 декабря 2008

Я пишу плагины Eclipse и часто сталкиваюсь с ситуацией, когда выполняющееся задание должно на короткое время приостановиться, выполнить что-то асинхронно в потоке пользовательского интерфейса и возобновить работу.

Так что мой код обычно выглядит примерно так:

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 было какое-то поле. Другой способ - использовать настраиваемый класс (а не анонимный для этого и использовать его результирующее поле данных и т. Д.). Какой самый лучший и самый элегантный подход?

Ответы [ 3 ]

8 голосов
/ 10 декабря 2008

Я думаю, что Контейнер выше - «правильный» выбор. Это также может быть обобщено для безопасности типов. Быстрый выбор в такой ситуации - это окончательный вариант массива. Хитрость заключается в том, что любые локальные переменные, на которые ссылается 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]);

Опять же, это «быстрое» решение для тех случаев, когда у вас есть анонимный класс, и вы хотите дать ему место для закрепления результата без определения определенного класса-контейнера.

UPDATE Подумав немного об этом, я понял, что это прекрасно работает для использования типа слушателя и посетителя, когда обратный вызов находится в одном потоке. В этом случае, однако, 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. Это подробно описано здесь .

4 голосов
/ 10 декабря 2008

Вы, вероятно, не должны предполагать, что асинхронное Runnable завершится к тому времени, когда вызов asyncExec завершится.

В этом случае вы смотрите на выдачу результата в слушатели / обратные вызовы (возможно, шаблон Command), или если вы хотите, чтобы результат был доступен позже тем же методом, используя что-то вроде java.util.concurrent.Future.

0 голосов
/ 10 декабря 2008

Что ж, если это синхронизация, вы можете просто иметь какой-либо держатель значения, внешний по отношению к методу 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;
  }
}

Это, конечно, весело и хитроумно (еще более хитроумно создавать новый список, а затем устанавливать и получать 1-й элемент), но метод syncExec блокирует, так что из этого ничего плохого не выйдет.

За исключением случаев, когда кто-то возвращается позже и делает это asyncExec() ..

...