параллельное выполнение scala futures с использованием цикла for

package p1
import scala.util.Failure
import scala.util.Success
import scala.concurrent.Await
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._

object modCheck extends App {

  def getStudentRoolNo(name: String) = Future {
    println("getStudentRoolNo")
    name match {
      case "name1" => 1
      case "name2" => 2
      case _       => throw new Exception("No doesnt exist")
    }
  }

  def getRank(roolNo: Int) = Future {
    println("getRank")
    Thread.sleep(500)
    roolNo match {
      case 1 => "1"
      case 2 => "2"
      case _ => throw new Exception("No roolNo exist")
    }
  }

  def getDetails(roolNo: Int) = Future {
    println("getDetails")
    roolNo match {
      case 1 => "details 1"
      case 2 => "Details 2"
      case _ => throw new Exception("No details exist")
    }
  }

  def getStudentRecord(name: String) = {
    for {
      rollNo <- getStudentRoolNo(name)
      rank <- getRank(rollNo)
      details <- getDetails(rollNo)
    } yield (rank + details)

  }

  getStudentRecord("name1").onComplete {
    case Success(ground) => println(s"got my Details $ground")
    case Failure(ex)     => println("Exception!" + ex)
  }

  Thread.sleep(2000)

}

Я хочу выполнять функции getrank и getDetails параллельно в приведенном ниже коде (после возврата getStudentRollNo). Как я могу этого добиться?

Я пробовал ниже, кажется, он все еще выполняется последовательно

Пожалуйста, дайте мне знать, как выполнить параллельно


person sharath chandra    schedule 07.10.2016    source источник


Ответы (3)


Будущее начинает вычисления, когда оно создано.

for (a <- x; b <- y) yield ??? обесценивается до x.flatMap(a => y.map(b => ???)) flatMap() и map() выполняет свой аргумент после завершения Future.

getDetails() можно начать до завершения getRank(), разделив создание Future и flatMap() вызовов.

for {
    rollNo <- getStudentRoolNo(name)
    rankFuture = getRank(rollNo)
    detailsFuture = getDetails(rollNo)
    rank <- rankFuture
    details <- detailsFuture
  } yield (rank + details)
person kawty    schedule 07.10.2016

Как вы, вероятно, догадались, ваш текущий код не вызывает параллельные вызовы getRank и getDetails, так как он находится внутри for-comprehension. Это синтаксический сахар для операции map. Чтобы добиться параллелизма, вам нужно создать два фьючерса вне понимания.

val student = getStudentRollNo(name)
val detailsFuture = student map {s => getRank(rollNo) }
val rankFuture = student map {s => getDetails(rollNo) }

for {
  rank <- rankFuture
  details <- detailsFuture
} yield (rank + details)
person Richeek    schedule 07.10.2016
comment
Спасибо за предложение, я больше смотрю на написание кода внутри цикла for (поскольку моя фактическая сложная логика во многом связана с этим, теперь принятие приведенного выше решения может быть немного сложным), я изменил вопрос, почему в этом случае также он выполняется последовательно? - person sharath chandra; 07.10.2016
comment
Затем можно переместить rankFuture = getRank(rollNo) и последующие строки, чтобы получить часть. Это уменьшит беспорядок и обеспечит параллельное выполнение обоих фьючерсов. - person Richeek; 07.10.2016
comment
for { rollNo ‹- getStudentRoolNo(name) rankFuture = getRank(rollNo) detailsFuture = getDetails(rollNo) rank ‹- rankFuture details ‹- detailsFuture } yield (rank + details) - Так работает, спасибо :) - person sharath chandra; 07.10.2016

Вы можете использовать zip в своем for-comprehension для параллельного запуска двух фьючерсов:

  def getStudentRecord(name: String) = {
    for {
      rollNo <- getStudentRoolNo(name)
      rankRollNo <- getRank(rollNo) zip getDetails(rollNo)
    } yield rankRollNo
  }

В приведенном выше getRank и getDetails выполняются одновременно, и результатом является строковый кортеж. Если вы хотите получить одну строку, вам нужно будет разделить rankRollNo на отдельные компоненты:

yield rankRollNo._1 + rankRollNo._2
person Chris C    schedule 17.10.2016