2016-12-20 2 views
1

У меня есть программа scala, которая выполняется некоторое время, а затем завершается. Я хотел бы предоставить библиотеку этой программе, которая за кулисами планирует асинхронную задачу для запуска каждые N секунд. Я также хотел бы, чтобы программа завершилась, когда работа точки входа main была закончена, без необходимости явно указывать фоновое действие для закрытия (поскольку оно находится внутри библиотеки).Идиоматическое планирование фоновой работы, которая умирает с основным потоком в Scala

Как я могу сказать, что идиоматический способ сделать опрос или запланированную работу в Scala с помощью Akka's ActorSystem.scheduler.schedule, но с помощью ActorSystem программа зависает после того, как main ждет актеров. Затем я попробовал и не смог добавить еще одного актера, который join s на основной нити, по-видимому, потому что "Anything that blocks a thread is not advised within Akka"

Я мог бы ввести пользовательский диспетчер; Я мог бы клонировать что-то вместе с опросом isAlive или добавить аналогичную проверку внутри каждого работника; или я мог бы отказаться от Акки и просто использовать сырые потоки.

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

+0

Вы можете просто заблокировать основной поток, используя 'Await.result (whenTerminated, Duration.Inf)' и при необходимости завершите завершение работы системы с актера. –

ответ

1

Я не думаю, что есть идиоматический способ Скала.

Программа JVM завершается, когда все потоки не-демона завершены. Таким образом, вы можете запланировать выполнение своей задачи в потоке демона.

Так просто использовать функциональные возможности Java:

import java.util.concurrent._ 

object Main { 

    def main(args: Array[String]): Unit = { 

    // Make a ThreadFactory that creates daemon threads. 
    val threadFactory = new ThreadFactory() { 
     def newThread(r: Runnable) = { 
     val t = Executors.defaultThreadFactory().newThread(r) 
     t.setDaemon(true) 
     t 
     } 
    } 

    // Create a scheduled pool using this thread factory 
    val pool = Executors.newSingleThreadScheduledExecutor(threadFactory) 

    // Schedule some function to run every second after an initial delay of 0 seconds 
    // This assumes Scala 2.12. In 2.11 you'd have to create a `new Runnable` manually 
    // Note that scheduling will stop, if there is an exception thrown from the function 
    pool.scheduleAtFixedRate(() => println("run"), 0, 1, TimeUnit.SECONDS) 

    Thread.sleep(5000) 
    } 
} 

Вы также можете использовать guava создать демон нить завод с new ThreadFactoryBuilder().setDaemon(true).build().

1

Если вы используете планировщик Akka, вы будете полагаться на высоко настраиваемую и оптимизированную реализацию, которая хорошо протестирована. Приведение в действие актерской системы - это немного тяжелый вес, хотя я согласен. Кроме того, вы должны зависеть от akka. Если у вас все в порядке, вы можете явно вызвать system.shutdown из основного, когда вы закончите, или обернуть его функцией, которая сделает это за вас.

В качестве альтернативы, вы можете попробовать что-то вдоль этих линий:

import scala.concurrent._ 
import ExecutionContext.Implicits.global 

object Main extends App { 

    def repeatEvery[T](timeoutMillis: Int)(f: => T): Future[T] = { 
     val p = Promise[T]() 
     val never = p.future 
     f 
     def timeout = Future { 
     Thread.sleep(timeoutMillis) 
     throw new TimeoutException 
     } 
     val failure = Future.firstCompletedOf(List(never, timeout)) 
     failure.recoverWith { case _ => repeatEvery(timeoutMillis)(f) } 
    } 

    repeatEvery(1000) { 
     println("scheduled job called") 
    } 

    println("main started doing its work") 
    Thread.sleep(10000) 
    println("main finished") 
} 

распечаток:

scheduled job called 
main started doing its work 
scheduled job called 
scheduled job called 
scheduled job called 
scheduled job called 
scheduled job called 
scheduled job called 
scheduled job called 
scheduled job called 
scheduled job called 
main finished 

мне не нравится, что он использует Thread.sleep, но это делается, чтобы избежать использования любого другого 3-й сторонних планировщиков и Scala Future не предоставляют параметры таймаута. Таким образом, вы будете тратить один поток на эту задачу планирования, но это то, что планировщик Akka кажется to do anyway. Разница в том, что, возможно, вам нужен один планировщик для всей JVM, чтобы не тратить слишком много потоков. Код, который я предоставил, хотя и проще, будет тратить поток на работу.

Смежные вопросы