【问题标题】:Quicksort using Future ends up in a deadlock使用 Future 的快速排序最终陷入僵局
【发布时间】:2012-12-03 20:51:32
【问题描述】:

我编写了一个快速排序(方法 quicksortF()),它使用 Scala 的 Future 让分区的递归排序同时进行。我还实现了一个常规的快速排序(方法quicksort())。不幸的是,当要排序的列表大于大约 1000 个元素(900 个可以工作)时,Future 版本最终会陷入死锁(显然永远阻塞)。源码如下图。

我对 Actors 和 Futures 比较陌生。这是怎么回事?

谢谢!

import util.Random
import actors.Futures._

/**
 * Quicksort with and without using the Future pattern.
 * @author Markus Gumbel
 */
object FutureQuickSortProblem {

  def main(args: Array[String]) {
    val n = 1000 // works for n = 900 but not for 1000 anymore.

    // Create a random list of size n:
    val list = (1 to n).map(c => Random.nextInt(n * 10)).toList
    println(list)
    // Sort it with regular quicksort:
    val sortedList = quicksort(list)
    println(sortedList)
    // ... and with quicksort using Future (which hangs):
    val sortedListF = quicksortF(list)
    println(sortedListF)
  }

  // This one works.
  def quicksort(list: List[Int]): List[Int] = {
    if (list.length <= 1) list
    else {
      val pivot = list.head
      val leftList = list.filter(_ < pivot)
      val middleList = list.filter(pivot == _)
      val rightList = list.filter(_ > pivot)

      val sortedLeftList = quicksort(leftList)
      val sortedRightList = quicksort(rightList)
      sortedLeftList ::: middleList ::: sortedRightList
    }
  }

  // Almost the same as quicksort() except that Future is used.
  // However, this one hangs.
  def quicksortF(list: List[Int]): List[Int] = {

    if (list.length <= 1) list
    else {
      val pivot = list.head
      val leftList = list.filter(_ < pivot)
      val middleList = list.filter(pivot == _)
      val rightList = list.filter(_ > pivot)

      // Same as quicksort() but here we are using a Future
      // to sort the left and right partitions independently:
      val sortedLeftListFuture = future {
        quicksortF(leftList)
      }
      val sortedRightListFuture = future {
        quicksortF(rightList)
      }
      sortedLeftListFuture() ::: middleList ::: sortedRightListFuture()
    }
  }
}

class FutureQuickSortProblem // If not defined, Intellij won't find the main method.?!

【问题讨论】:

  • PS:我使用的是 Scala 2.9.2 和 JRE 1.6_22

标签: scala deadlock actor future


【解决方案1】:

免责声明:我从未以任何严肃的方式亲自使用过(2.10 之前的)标准库的 actor 或 future,而且我不喜欢(或至少不理解)一些关于那里的 API,例如与 Scalaz 或 Akka 或 Play 2.0 中的实现相比。

但我可以告诉你,在这种情况下,通常的方法是单子组合你的未来,而不是立即声明它们并组合结果。例如,你可以这样写(注意新的返回类型):

import scala.actors.Futures._

def quicksortF(list: List[Int]): Responder[List[Int]] = {
  if (list.length <= 1) future(list)
  else {
    val pivot = list.head
    val leftList = list.filter(_ < pivot)
    val middleList = list.filter(pivot == _)
    val rightList = list.filter(_ > pivot)

    for {
      left <- quicksortF(leftList)
      right <- quicksortF(rightList)
    } yield left ::: middleList ::: right
  }
}

就像您的普通实现一样,这不一定非常有效,而且它也会很容易地破坏堆栈,但它不应该用完线程。

作为旁注,为什么Future 上的flatMap 返回Responder 而不是Future?我不知道,还有neither do some other folks。出于这样的原因,我建议完全跳过现已弃用的 2.10 之前标准库基于参与者的并发内容。

【讨论】:

  • 特拉维斯,谢谢你的提议!我还没有使用过Responder,需要了解它的作用。
  • ResponderFuture 的超类型,本质上是一个Future,你不能用apply 声明。
【解决方案2】:

据我了解,在 Future 上调用 apply(就像您在连接递归调用的结果时所做的那样)将阻塞,直到检索到结果。

【讨论】:

  • 感谢您的回答。是的,这也是我的理解。但这意味着当两个列表(左和右)都被排序时,递归调用应该返回。只有在所有结果可用之前,才应阻止调用。而这似乎并非如此。
  • 啊,我的描述有点误导。它不仅阻塞,而且以死锁结束。文本已更改。
  • 没有加锁,怎么会死锁呢?并且阻塞不是必需的,因为分区是通过构造不相交的,因此递归调用不会重叠或以其他方式干扰。
  • 每个递归步骤都会阻塞一个线程,直到您无法分配更多线程;不要使用 Future.apply(在 2.9 中)或 Await.result(在 2.10 中等效),这些方法仅用于紧急测试用例(即您无法以其他方式制定的测试用例),绝不用于生产代码。
  • 技术术语不是“死锁”而是“线程饥饿”,但改名并没有改变问题的原因;-)
猜你喜欢
  • 1970-01-01
  • 2022-07-14
  • 2020-07-12
  • 2020-04-07
  • 1970-01-01
  • 1970-01-01
  • 2019-04-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多