2013-05-03 6 views
28

私は将来の予定をScalaで実装しようとしています。私は特定の時間を待ってから体を実行したいと思います。これまでのところ、私は次の単純なアプローチを試した。Scala - ScheduledFuture

val d = 5.seconds.fromNow 

val f = future {Await.ready(Promise().future, d.timeLeft); 1} 

val res = Await.result(f, Duration.Inf) 

私は将来TimeoutExcpetionを取得している。これは正しいアプローチですか、それとも単にJavaからScheduledExecutorを使うべきですか?

答えて

5

あなたはこのような何かにあなたのコードを変更することができます:

val d = 5.seconds.fromNow 
val f = Future {delay(d); 1} 
val res = Await.result(f, Duration.Inf) 

def delay(dur:Deadline) = { 
    Try(Await.ready(Promise().future, dur.timeLeft)) 
} 

しかし、私はそれをお勧めしません。そうすることで、あなたは未来をブロックすることになるでしょう。ブロックすることは決して完了しません。私はあなたが述べたようにJavaのスケジュールされたエグゼキュータを使って調べるか、Akkaを@ alex23として推奨することを検討することができます。

+5

'待ちます.ready'は 'blocking 'を使用しています。したがって、5秒間作業している場合、少なくとも基礎となるプールがそのスレッドをスピンアップできます。 –

+1

この(または元の質問)がうまくいくために必要な輸入品は見やすいものです。私は彼らがscala.concurrent.duration._とscala.concurrentだと思う。_ – akauppi

15

標準ライブラリだけを使用して、それをそのまま使用することは何もありません。 Javaの予定の先物とは異なり、この実装はさせないことを

import scala.concurrent.duration._ 
import scala.concurrent.ExecutionContext.Implicits._ 

DelayedFuture(5 seconds)(println("Hello")) 

注:これは、このように使用することができます

object DelayedFuture { 
    import java.util.{Timer, TimerTask} 
    import java.util.Date 
    import scala.concurrent._ 
    import scala.concurrent.duration.FiniteDuration 
    import scala.util.Try 

    private val timer = new Timer(true) 

    private def makeTask[T](body: => T)(schedule: TimerTask => Unit)(implicit ctx: ExecutionContext): Future[T] = { 
    val prom = Promise[T]() 
    schedule(
     new TimerTask{ 
     def run() { 
      // IMPORTANT: The timer task just starts the execution on the passed 
      // ExecutionContext and is thus almost instantaneous (making it 
      // practical to use a single Timer - hence a single background thread). 
      ctx.execute( 
      new Runnable { 
       def run() { 
       prom.complete(Try(body)) 
       } 
      } 
     ) 
     } 
     } 
    ) 
    prom.future 
    } 
    def apply[T](delay: Long)(body: => T)(implicit ctx: ExecutionContext): Future[T] = { 
    makeTask(body)(timer.schedule(_, delay)) 
    } 
    def apply[T](date: Date)(body: => T)(implicit ctx: ExecutionContext): Future[T] = { 
    makeTask(body)(timer.schedule(_, date)) 
    } 
    def apply[T](delay: FiniteDuration)(body: => T)(implicit ctx: ExecutionContext): Future[T] = { 
    makeTask(body)(timer.schedule(_, delay.toMillis)) 
    } 
} 

: 最も簡単なユースケースの場合、あなたはこのような小さなヘルパーを使用することができますあなたは未来をキャンセルします。

+1

私は1つのことを理解していません: '暗黙のctx:ExecutionContext'引数がありますが、どこで使用されるのか分かりません。' makeTask'や 'timer.schedule'を期待していません。 – Suma

+0

それを指摘してくれてありがとう、それは明らかに誤りです。私の推測では、私が行ったリファクタリングから残ったものである(何らかの理由でどこかで 'map' /' flatMap'をどこかに持っていたかもしれませんが)、私はこれを書いてから、 。無用なExecutionContextを削除するためにスニペットを更新しました。 –

+1

私はもっと真剣に考えていましたが、実際にはExecutionContextを渡す必要があります。原因はTimerのバックグラウンドスレッドで実行するのではなく、ExecutionContextで本体を実行する必要があります。つまり、アプリケーション全体で遅れている未来のスレッドが1つしかなかったということです。たとえそれが本質的に単なる例であったとしても、それは私のところではうんざりです。新しいバージョンはより生産性の高いものです。 –

6

私のソリューションは、レジスのにかなり似ていますが、私はスケジュールにアッカを使用します。

def delayedFuture[T](delay: FiniteDuration)(block: => T)(implicit executor : ExecutionContext): Future[T] = { 
    val promise = Promise[T] 

    Akka.system.scheduler.scheduleOnce(delay) { 
     try { 
     val result = block 
     promise.complete(Success(result)) 
     } catch { 
     case t: Throwable => promise.failure(t) 
     } 
    } 
    promise.future 
    } 
52

アッカがakka.patternました:

def after[T](duration: FiniteDuration, using: Scheduler)(value: ⇒ Future[T])(implicit ec: ExecutionContext): Future[T] 

は「完成する予定scala.concurrent.Futureを返します。指定された期間の後に提供された値が成功したか失敗したかを示します。

このため

http://doc.akka.io/api/akka/2.2.1/#akka.pattern.package

+2

この回答は最良の回答としてマークする必要があります。 – Somatik

+8

これはアクターシステムなしで使用できますか? – rightfold

+1

これは並行性のためにこれがうまくいかない理由は何もありません。これは役に立ちません。 – matanster

-4

最短ソリューション、おそらくスカラ・非同期を利用している:

import scala.async.Async.{async, await} 

def delay[T](value: T, t: duration): Future[T] = async { 
    Thread.sleep(t.toMillis) 
    value 
} 

やケースには、あなたがブロックの遅延実行をしたい

def delay[T](t: duration)(block: => T): Future[T] async { 
    Thread.sleep(t.toMillis) 
    block() 
} 
+2

これは非常に不十分な解決策です。既に述べたように、これはスレッドをブロックします。 –

+1

2つのもの: 両方のケースでネストされた{...}が欠けていませんか?これにより、スレッドがブロックされなくなり、実際に遅延が発生する可能性があります。 また、潜在的なデッドロックを防ぐために、ブロック{...}ブロックにラップされたThread.sleep(...)呼び出しでFutureを構築するだけではどうですか? –

2

他のすべてソリューションはakkaを使用するか、遅延タスクごとにスレッドをブロックします。より良い解決策(既にakkaを使用している場合を除く)は、javaのScheduledThreadPoolExecutorを使用することです。ここではそのためのScalaラッパーの例です:

https://gist.github.com/platy/8f0e634c64d9fb54559c

+1

私の答えは、akkaなしでもスレッドをブロックする方法を示し、1年以上前に投稿されました –

+0

ありがとう私はあなたの答えを忘れている必要があります、私はそれが好きです、実行のためのスレッドプールが必要です私のソリューションで。 – MikeB

13

あなたがアッカせずに完了のスケジュールを設定したい場合は、あなたが完了するために約束をスケジュールするために、通常のJavaタイマーを使用することができます。

def delay[T](delay: Long)(block: => T): Future[T] = { 
    val promise = Promise[T]() 
    val t = new Timer() 
    t.schedule(new TimerTask { 
    override def run(): Unit = { 
     promise.complete(Try(block)) 
    } 
    }, delay) 
    promise.future 
} 
+0

あなたは構文について確かですか?私は 'ブロック'は関数ではないと思うので、 'block()'として使うのではなく、 'ブロック'として使うべきです。 – Suma

+1

また、私のコメントhttp://stackoverflow.com/a/16363444/16673に似ています - あなたは 'executor'をどこかで使っていますか? – Suma

+1

@Sumaそれは私がテストせずにメモリからコードを書くことです。私はコードを修正し、それが私がそれを行うと主張したことを確実にした。 –