在Scala中正确使用.par吗?

时间:2015-08-14 12:30:39

标签: scala

下面的代码计算两个用户之间的距离度量,由案例类指定:

  case class User(name: String, features: Vector[Double])

  val ul = for (a <- 1 to 100) yield (User("a", Vector(1, 2, 4)))
  var count = 0;

  def distance(userA: User, userB: User) = {
    val subElements = (userA.features zip userB.features) map {
      m => (m._1 - m._2) * (m._1 - m._2)
    }
    val summed = subElements.sum
    val sqRoot = Math.sqrt(summed)
    count += 1;
    println("count is " + count)

    ((userA.name, userB.name), sqRoot)
  }

  val distances = ul.par.map(m => ul.map(m2 => {
    (distance(m, m2))
  })).toList.flatten

  val sortedDistances = distances.groupBy(_._1._1).map(m => (m._1, m._2.sortBy(s => s._2)))

  println(sortedDistances.get("a").get.size);

这执行比较100个用户的笛卡尔积:10000比较。我计算每次比较,代表bu var count 计数值通常小于10000,但迭代的项数总是10000.这是因为par产生多个线程,其中一些将在执行println语句之前完成。但是,所有这些都将在par代码块内完成 - 在评估distances.groupBy(_._1._1).map(m => (m._1, m._2.sortBy(s => s._2)))之前。

1 个答案:

答案 0 :(得分:0)

在你的例子中,你有一个单独的非同步变量,你正在从多个线程变异,就像你说的那样。这意味着每个线程在任何时候都可能具有count的陈旧副本,因此当它们递增时,它们将压缩已发生的任何其他写入,从而导致计数小于它应该的数量。

您可以使用synchronized函数

解决此问题
...
    val subElements = (userA.features zip userB.features) map {
      m => (m._1 - m._2) * (m._1 - m._2)
    }
    val summed = subElements.sum
    val sqRoot = Math.sqrt(summed)
    this.synchronized {
      count += 1;    
    }    

    println("count is " + count)

    ((userA.name, userB.name), sqRoot)
...

使用'this.synchronized'将使用包含对象作为锁定对象。有关Scala同步的更多信息,建议您阅读Twitter's Scala School.