JavaFx2 или ScalaFx + Akka

Как запустить актеров Akka в приложении JavaFX/ScalaFX?

(Это обновление вопроса, основанного на первых ответах)

Является ли решение распространять один и тот же контекст выполнения? Что означает наличие диспетчеров Actors на базе JavaFx ExecutorService? (Тот, с которым он запускает код манипуляции с пользовательским интерфейсом)

Означает ли это, что один агент будет представлять пользовательский интерфейс и сможет его манипулировать? Я имею в виду, что, предположив ниже, если пара действующих лиц находится в UI ExecutorService, не означает ли это, что между агентом (объектом является пользовательский интерфейс) существует состояние?

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

Наконец, почему использование akka as is, с его контекстом Executor по-разному и с использованием Platform.runLater, может иметь некоторые последствия для производительности пользовательского интерфейса. Я задаю вопрос о нескольких службах-исполнителях в одном приложении: это плохо?

Ответ 1

  • Фьючерс

Лучший способ использовать scala Фьючерсы вместе с однопоточным инструментарием, таким как JavaFX, состоят в том, чтобы определить исполнителя, который позволяет выполнять фьючерсы или актеры в потоке инструментария пользовательского интерфейса.

Та же проблема существует и для Swing, которая также требует обновления обновлений для потока swing. Виктор Кланг придумал следующее решение Контекст качания Swing. Здесь он переводится для JavaFX:

import akka.dispatch.ExecutionContext
import javafx.application.Platform
import java.util.concurrent.Executor

//
object JavaFXExecutionContext {
  implicit val javaFxExecutionContext: ExecutionContext = ExecutionContext.fromExecutor(new Executor {
  def execute(command: Runnable): Unit = Platform.runLater(command)
  })
}

Вы бы использовали его следующим образом:

// import the default ec
import scala.concurrent.ExecutionContext.Implicits.global
// define the JavaFX ec so we can use it explicitly
val fxec = JavaFXExecutionContext.javaFxExecutionContext
future {
  // some asynchronous computation, running on the default
  // ForkJoin ExecutionContext because no ec is passed
  // explicitly
}.map(result => {
  // update JavaFX components from result
  // This will run in the JavaFX thread.
  // Check Platform.isFxApplicationThread() to be sure!
})(fxec)

Конвейер фьючерсов может быть очень сложным, если шаги (шаги), которые взаимодействуют с компонентами JavaFX, все запущены в JavaFX ExecutionContext.

Примечание. Это зависит от вас, внесите ли вы значение ForkJoin ec по умолчанию и передайте JavaFX ec явно или наоборот. Возможно, было бы неплохо сделать JavaFX ec по умолчанию для предотвращения ошибок и отметить части, которые могут выполняться асинхронно с помощью ForkJoin ec.

  • Актеры

Для интеграции системы на основе актера с однопоточным инструментарием пользовательского интерфейса существует также решение. Смотрите Свинг-актеры. Все, что вам нужно сделать, это заменить

SwingUtilities.invokeLater(command)

с

Platform.runLater(command)

и вам хорошо идти!

  • Когда использовать

Если у вас есть большое приложение пользовательского интерфейса и вы хотите просто отключить некоторые асинхронные операции (загрузка файла или выполнение некоторых вычислений), вероятно, предпочтителен подход на основе фьючерсов. Но будьте осторожны, чтобы не взаимодействовать (ни читать, ни писать) с компонентами JavaFX во фьючерсах, которые выполняются асинхронно в контексте выполнения по умолчанию.

Если у вас есть большая система на основе актеров и вы просто хотите подключить пользовательский интерфейс к некоторым частям, возможно, несколько предпочтительных участников, работающих по потоку JavaFX. YMMV.

Ответ 2

Есть несколько вещей, которые вам нужно учитывать при использовании нескольких потоков в JavaFX:

  • Любой код, который заканчивается касанием графа сцены (например, путем обновления данных, привязанных к элементам управления), должен быть завернут в Platform.runLater. Если вы используете встроенный многопоточный API в JavaFX (т.е. Task и Service), это будет сделано автоматически, но если вы используете любую другую многопоточную утилиту, вы должны сделать это самостоятельно.

  • Ваша многопоточная утилита (т.е. Akka) должна (я думаю) как-то сказать, чтобы оставить место для потока событий JavaFX. Если вы посмотрите на источник Service, вы увидите, что при настройке исполнителя они проявляют определенную осторожность. Я не уверен в деталях этого, но когда я экспериментировал с использованием Scala Future с JavaFX, я заметил некоторую невосприимчивость в пользовательском интерфейсе при использовании по умолчанию ExecutionContext, который исчез, когда я использовал пользовательский в реализации Service.

В ScalaFX (или любом другом наборе инструментов, насколько я знаю) нет поддержки для работы с Scala Futures или Akka таким образом, чтобы вы могли забыть о двух указанных выше пунктах, но было бы интересно, посмотрите.

Ответ 3

Здесь суть сущности аккеры, которые можно запускать в потоке Swing или JavaFX. Это удобное копируемое расширение Виктора Клана Swing Actors на основе Ответ Рюдигера.