JavaFx2 или ScalaFx + Akka
Как запустить актеров Akka в приложении JavaFX/ScalaFX?
(Это обновление вопроса на основе первых ответов)
Является ли решение использовать тот же контекст выполнения? Имеется в виду, что диспетчеры Actors основаны на JavaFx ExecutorService? (Тот, с которым он выполняет код манипуляции пользовательского интерфейса)
Означает ли это, что один агент будет представлять пользовательский интерфейс и сможет им манипулировать? Я имею в виду, потому что предлагается ниже, если пара актеров находится в пользовательском интерфейсе ExecutorService, разве это не означает разделять состояние между агентом (объект является пользовательским интерфейсом)?
Могут ли 2 актера общаться, находясь на разных службах исполнителя? Я спрашиваю об этом, потому что из того, что предлагается ниже, некоторые агенты будут в UI Executor Service, а другие нет.
Наконец, почему использование akka "как есть" с различным контекстом в Executor и использованием Platform.runLater может повлиять на производительность пользовательского интерфейса. Я задаю вопрос о множестве исполнителей в одном приложении: это плохо?
3 ответа
- фьючерсы
Лучший способ использовать scala Futures вместе с однопоточным инструментарием, таким как JavaFX, - это определить исполнителя, который позволит вам выполнять фьючерсы или акторы в потоке UI Toolkit.
Та же проблема существует для Swing, которая также требует обновления для потока Swing. Виктор Кланг предложил следующее решение Swing Execution Context. Вот это переводится для 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 по умолчанию и передать JavaFX ec явным образом или наоборот. Было бы неплохо сделать JavaFX ec по умолчанию, чтобы предотвратить ошибки и пометить части, которые могут работать асинхронно явно с помощью ForkJoin ec.
- Актеры
Для интеграции системы на основе Actor с однопоточным инструментарием пользовательского интерфейса также существует решение. Смотрите Swing Actors. Все, что вам нужно сделать, это заменить
SwingUtilities.invokeLater(command)
с
Platform.runLater(command)
и ты в порядке!
- Когда использовать какой
Если у вас большое приложение пользовательского интерфейса и вы просто хотите выполнить некоторые асинхронные операции (загрузка файла или выполнение некоторых вычислений), подход на основе фьючерсов, вероятно, предпочтительнее. Но будьте осторожны, чтобы не взаимодействовать (ни читать, ни писать) с компонентами JavaFX во фьючерсах, которые работают асинхронно в контексте выполнения по умолчанию.
Если у вас большая система, основанная на актерах, и вы просто хотите присоединить пользовательский интерфейс к некоторым частям, вероятно, предпочтительнее иметь несколько действующих лиц, работающих в потоке JavaFX. YMMV.
Есть несколько вещей, которые вы должны учитывать при использовании нескольких потоков в JavaFX:
Любой код, который заканчивается касанием графа сцены (например, путем обновления данных, связанных с элементами управления), должен быть заключен в
Platform.runLater
, Если вы используете встроенный многопоточный API в JavaFX (т.е.Task
а такжеService
) это будет сделано автоматически, но если вы используете любую другую многопоточную утилиту, вы должны сделать это самостоятельно.Ваша многопоточная утилита (например, Akka) должна (как мне кажется) как-то сказать оставить место для потока событий JavaFX. Если вы посмотрите на источник для Service, вы увидите, что они очень внимательно относятся к настройке исполнителя. Я не уверен в деталях этого, но когда я экспериментировал с использованием Scala
Future
с JavaFX, я заметил некоторую неотзывчивость в пользовательском интерфейсе при использовании по умолчаниюExecutionContext
, который исчез, когда я использовал пользовательский на основеService
реализация.
В ScalaFX (или, насколько мне известно, в любом другом наборе инструментов) нет поддержки для работы с Scala Futures или Akka таким образом, чтобы вы могли забыть о двух вышеперечисленных пунктах, но, безусловно, было бы интересно посмотреть.
Вот суть актеров akka, которые могут быть запущены в потоке Swing или JavaFX. Это удобное копируемое расширение актеров свинга Виктора Кланга, основанное на ответе Рюдигера.