簡體   English   中英

Scala未來序列和超時處理

[英]Scala future sequence and timeout handling

如何將期貨與超時結合起來有一些很好的提示。 但是我很好奇如何用Future sequence sequenceOfFutures做到這一點

我的第一種方法看起來像這樣

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

object FutureSequenceScala extends App {
  println("Creating futureList")

  val timeout = 2 seconds
  val futures = List(1000, 1500, 1200, 800, 2000) map { ms =>
    val f = future {
      Thread sleep ms
      ms toString
    }
    Future firstCompletedOf Seq(f, fallback(timeout))
  }

  println("Creating waitinglist")
  val waitingList = Future sequence futures
  println("Created")

  val results = Await result (waitingList, timeout * futures.size)
  println(results)

  def fallback(timeout: Duration) = future {
    Thread sleep (timeout toMillis)
    "-1"
  }
}

有沒有更好的方法來處理一系列期貨中的超時或這是一個有效的解決方案?

您的代碼中有一些內容可能需要重新考慮。 對於初學者來說,我不是將任務提交到ExecutionContext中的忠實粉絲,其唯一目的是模擬超時並且還使用了Thread.sleep sleep調用是阻塞的,您可能希望避免在執行上下文中有一個任務,為了等待一段固定的時間而完全阻塞。 我將從這里的答案中竊取並建議對於純超時處理,你應該使用我在答案中概述的內容。 HashedWheelTimer是一個高效的計時器實現,比只是睡眠的任務更適合超時處理。

現在,如果你走這條路線,下一次改變我會建議處理每個未來的個別超時相關故障。 如果您希望單個故障完全失敗,則從sequence調用返回的聚合Future ,則不執行任何額外操作。 如果您不希望發生這種情況,而是希望超時返回一些默認值,那么您可以在Future上使用recover ,如下所示:

withTimeout(someFuture).recover{
  case ex:TimeoutException => someDefaultValue
}

完成后,您可以利用非阻塞回調並執行以下操作:

waitingList onComplete{
  case Success(results) => //handle success
  case Failure(ex) => //handle fail
}

每個未來都有一個超時,因此不會無限運行。 沒有必要IMO阻止那里並通過atMost參數向Await.result提供額外的超時處理層。 但我想這可以假設你對非阻塞方法沒問題。 如果你真的需要在那里阻止,那么你不應該等待timeout * futures.size的時間量。 這些期貨並行運行; 那里的超時應該只需要與期貨本身的個別超時一樣長(或者只是稍微長一點來解釋cpu /時間的任何延遲)。 它當然不應該是超時*期貨的總數。

這是一個顯示阻止fallback有多糟糕的版本。

請注意,執行程序是單線程的,您正在創建許多后備。

@cmbaxter是對的,你的主超時不應該timeout * futures.size ,它應該更大!

@cmbaxter也是對的,你想要非阻塞。 一旦你這樣做,並且你想要施加超時,那么你將選擇一個計時器組件,查看他的鏈接答案(也鏈接到你的鏈接答案)。

也就是說,我仍然喜歡你的鏈接中的答案 ,只要坐在循環中等待下一個應該超時的事情真的很簡單。

它只需要一份期貨清單及其超時和后備價值。

也許有一個用例,例如一個簡單的應用程序,只是阻止某些結果(如你的測試),並且必須在結果出來之前退出。

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

import java.util.concurrent.Executors
import java.lang.System.{ nanoTime => now }

object Test extends App { 
  //implicit val xc = ExecutionContext.global
  implicit val xc = ExecutionContext fromExecutorService (Executors.newSingleThreadExecutor)

  def timed[A](body: =>A): A = {
    val start = now 
    val res = body
    val end = now
    Console println (Duration fromNanos end-start).toMillis + " " + res
    res
  }
  println("Creating futureList")

  val timeout = 1500 millis
  val futures = List(1000, 1500, 1200, 800, 2000) map { ms =>
    val f = future {
      timed {
        blocking(Thread sleep ms)
        ms toString
      }
    } 
    Future firstCompletedOf Seq(f, fallback(timeout))
  }   

  println("Creating waitinglist")
  val waitingList = Future sequence futures
  println("Created")

  timed {
  val results = Await result (waitingList, 2 * timeout * futures.size)
  println(results)
  }     
  xc.shutdown

  def fallback(timeout: Duration) = future {
    timed {
      blocking(Thread sleep (timeout toMillis))
      "-1"
    }
  }   
}   

發生了什么:

Creating futureList
Creating waitinglist
Created
1001 1000
1500 -1
1500 1500
1500 -1
1200 1200
1500 -1
800 800
1500 -1
2000 2000
1500 -1
List(1000, 1500, 1200, 800, 2000)
14007 ()

Monix Task有超時支持:

  import monix.execution.Scheduler.Implicits.global
  import monix.eval._
  import scala.concurrent.duration._

  println("Creating futureList")
  val tasks = List(1000, 1500, 1200, 800, 2000).map{ ms =>
    Task {
      Thread.sleep(ms)
      ms.toString
    }.timeoutTo(2.seconds, Task.now("-1"))
  }

  println("Creating waitinglist")
  val waitingList = Task.gather(tasks) // Task.sequence is true/literally "sequencing" operation

  println("Created")
  val results = Await.result(waitingList, timeout * futures.size)
  println(results)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM