2013-06-22 12 views
16

Jaki jest najprostszy sposób na opóźnienie wykonywania funkcji w Scali, podobnie jak JavaScript setTimeout? Idealnie bez wątku odradzania na opóźnione wykonanie, tj. Wykonanie sekwencyjne. Najbliższe, co udało mi się znaleźć, było Akka Scheduler, ale to przesada.Wykonanie funkcji opóźnienia

Dla celów testowych otwieram tysiące połączeń, a następnie otrzymują odpowiedzi w 10 sekund. W pliku node.js wygląda on następująco:

http.createServer(function (req, res) { 
    res.writeHead(200, {'Content-Type': 'text/plain'}); 
    setTimeout(function() {res.end('Hello World\n');}, 10000); 
}).listen(8080, '127.0.0.1'); 

Ale jaka byłaby najbliższa wersja Scala robiąc to samo? Nie obchodzi mnie, czy res.end zostanie wykonane w wielu wątkach lub kolejkowane w jednym.

+1

Możliwy duplikat, nie jestem pewien, że ta odpowiedź pomaga: http://stackoverflow.com/a/16629357/1296806 ale może jeśli chcesz ćpuna jednorazowe do testów. –

+0

Możesz również rzucić okiem na to inne pytanie: http://stackoverflow.com/questions/16359849/scala-scheduledfuture –

Odpowiedz

20

Zmęczony coraz flak dla odpowiedzi na pytanie o najprostsze zbyt prosto, tutaj są standardowe idiomy JVM:

$ scala 
Welcome to Scala 2.11.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_65). 
Type in expressions for evaluation. Or try :help. 

scala> import java.util.{Timer,TimerTask} 
import java.util.{Timer, TimerTask} 

scala> val timer = new Timer 
timer: java.util.Timer = [email protected] 

scala> def delay(f:() => Unit, n: Long) = timer.schedule(new TimerTask() { def run = f() }, n) 
delay: (f:() => Unit, n: Long)Unit 

scala> delay(() => println("Done"), 1000L) 

scala> Done 


scala> import java.util.concurrent._ 
import java.util.concurrent._ 

scala> val x = Executors.newScheduledThreadPool(2) 
x: java.util.concurrent.ScheduledExecutorService = [email protected] 

scala> x.schedule(new Callable[Int]() { def call = { println("Ran"); 42 }}, 1L, TimeUnit.SECONDS) 
res3: java.util.concurrent.ScheduledFuture[Int] = java.[email protected]3ab0f534 

scala> Ran 

Nie ma API do planowania opóźnionego zadania w bibliotece standardowej, ale można dokonać ExecutionContext ze stałym opóźnieniem, w celu użycia Scala Future.

scala> import scala.concurrent._ 
import scala.concurrent._ 

scala> implicit val xx = new ExecutionContext() { 
    | def reportFailure(t: Throwable) = t.printStackTrace() 
    | def execute(r: Runnable) = x.schedule(new Callable[Unit]() { def call = r.run() }, 1L, TimeUnit.SECONDS) 
    | } 
xx: scala.concurrent.ExecutionContext = [email protected] 

scala> Future(println("hello")) 
res4: scala.concurrent.Future[Unit] = List() 

scala> hello 

scala> Future(42) 
res5: scala.concurrent.Future[Int] = List()     

scala> .value 
res6: Option[scala.util.Try[Int]] = Some(Success(42)) 

Albo można użyć programu planującego Akka, która jest odpowiedzią kanoniczna w Scheduled Executor in Scala

stary-liner:

Najprostszy jest nadal tylko future { blocking(Thread.sleep(10000L)); "done" }

ale chciałem umieścić Reklama dla tego faceta, którego właśnie spotkałem, co daje ci wskaźnik postępu lub wartość pośrednią. Żałuję, że nie ma innej nazwy, to wszystko.

scala> import concurrent._ 
import concurrent._ 

scala> import ExecutionContext.Implicits._ 
import ExecutionContext.Implicits._ 

scala> import duration._ 
import duration._ 

scala> val deadline = 60.seconds.fromNow 
deadline: scala.concurrent.duration.Deadline = Deadline(38794983852399 nanoseconds) 

scala> new DelayedLazyVal(() => deadline.timeLeft.max(Duration.Zero), blocking { 
    | Thread.sleep(deadline.timeLeft.toMillis) 
    | Console println "Working!" 
    | }) 
res9: scala.concurrent.DelayedLazyVal[scala.concurrent.duration.FiniteDuration] = [email protected] 

scala> res9() 
res10: scala.concurrent.duration.FiniteDuration = 23137149130 nanoseconds 

scala> res9.isDone 
res11: Boolean = false 

scala> res9() 
res12: scala.concurrent.duration.FiniteDuration = 12499910694 nanoseconds 

scala> res9() 
res13: scala.concurrent.duration.FiniteDuration = 5232807506 nanoseconds 

scala> Working! 


scala> res9.isDone 
res14: Boolean = true 

scala> res9() 
res15: scala.concurrent.duration.FiniteDuration = 0 days 

Oto alternatywne sformułowanie z Obowiązkiem, aby obliczyć wartość po opóźnieniu. Oczywiście, gdy jest jeszcze czas Left.

scala> new DelayedLazyVal(()=> if (deadline.hasTimeLeft) Left(deadline.timeLeft) else 
    | Right("Working!"), blocking(Thread.sleep(deadline.timeLeft.toMillis))) 
res21: scala.concurrent.DelayedLazyVal[Product with Serializable with scala.util.Either[scala.concurrent.duration.FiniteDuration,String]] = [email protected] 

scala> res21() 
res22: Product with Serializable with scala.util.Either[scala.concurrent.duration.FiniteDuration,String] = Left(28553649064 nanoseconds) 

scala> res21() 
res23: Product with Serializable with scala.util.Either[scala.concurrent.duration.FiniteDuration,String] = Left(9378334087 nanoseconds) 

scala> res21.isDone 
res24: Boolean = false 

scala> res21() 
res25: Product with Serializable with scala.util.Either[scala.concurrent.duration.FiniteDuration,String] = Right(Working!) 

scala> res21.isDone 
res26: Boolean = true 
+6

czy to nie spowoduje tysięcy wątków śpiących przez 10 sekund? –

+3

Czy istnieje rozwiązanie bez blokady? –

+0

@OlegMichheev Tak. –