【问题标题】:How to prevent Future from never finishing如何防止 Future 永远不会完成
【发布时间】:2019-10-26 12:11:39
【问题描述】:

假设我有多个要并行运行的任务。

每个任务(方法)都有一个内部递归函数,它基本上从数据库中获取数据并将其保存在一些数据存储中。

[简化的内递归函数]

 def simplifiedSomeTask(): Unit = {
    @scala.annotation.tailrec
    def get(
        stream: Stream[SomeEntity],
        result: Seq[SomeEntity],
    ): Stream[SomeEntity] = result match {
      case Nil =>
        stream
      case _ =>
        val query = //query to fetch data from database
        get(
          stream append result.toStream,
          query.run.value, // get fetched data from database
        )
    }

    val buffer = collection.mutable.Map.empty[String, String]

    get(
      Stream.empty,
      query.run.value
    ).foreach { r =>
      buffer.put(r.loginId, r.userId)
    }
  }

当尝试运行 A 时,Future 出于某种原因永远不会结束。

[A]

val f1 =Future { someTask1() }
val f2 =Future { someTask2() }
val f3 =Future { someTask3() }

val f = for {
  _ <- f1 
  _ <- f2 
  _ <- f3 
} yield ()

Await.result(f, Duration.Inf)

但是,B 可以工作(尽管它不是并行运行的)

[B]

val f = for {
  _ <- Future { someTask1() }
  _ <- Future { someTask2() }
  _ <- Future { someTask3() }
} yield ()

Await.result(f, Duration.Inf)

我应该如何修改 A 使​​其按预期运行?

【问题讨论】:

    标签: scala future


    【解决方案1】:

    我无法重现您的问题,但奇怪行为的原因可能是您在第一个示例中的语法不完全正确。你应该写你的第一个理解,如:

    val f = for {
      _ <- f1
      _ <- f2
      _ <- f3
    } yield ()
    

    但是为了理解是按顺序工作的,在第一个示例中您的期货并行运行的唯一原因是期货急切地开始(“未来开始现在”)。

    如果您想确保 Futures 将并行执行,请使用 Future.sequence:

    val f = Future.sequence(
      List(
        Future { someTask1() },
        Future { someTask2() },
        Future { someTask3() }
      )
    )
    

    【讨论】:

    • 哦,我粘贴不正确。现在我更正了代码。
    • 不过,它应该可以工作,所以问题一定出在其他地方。您能分享一下您未来的工作吗?
    • 我添加了一个我正在使用的简化内部递归函数
    • 这个函数返回Future[Unit],是吗?
    • 不,它只返回 Unit。该函数最终被包裹在 Future 中,所以最终,Future[Unit] 准确地说是
    【解决方案2】:

    问题不在于理解,而在于你的任务。并行运行它们可能会出现某种死锁,但我会先三重检查它们是否最终不会进入无限循环。查看您的示例,如果query.run.value 永远不会返回空,则很容易发生这种情况,然后递归将永远继续。如果f1f2f3 中的任何个不解决,那么f 当然也永远不会解决。

    【讨论】:

    • 什么可能导致死锁?每个任务相互独立,从不同的表中获取记录,而不是从同一个表中。
    • 就像我说的,虽然有可能,但死锁似乎不太可能。您的示例代码中有一个神奇的val query = //query to fetch data from database。你能证明你的变量总是被分配一个迟早返回空的查询吗?如果没有,请从那里开始,因为如果您不知道您的代码将终止,那么寻找死锁将是浪费时间。
    【解决方案3】:

    事实证明,在创建 query 对象时,一些循环引用导致了这个问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-01-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-29
      相关资源
      最近更新 更多