首页 文章

这段代码中是否存在潜在的饥饿或仅仅是我?

提问于
浏览
2

我正在努力学习Scala,并发现它是一种很棒的语言 . 我从大卫波拉克那里学习"Beginning Scala" . 在第3章中有一段代码,它说明了如何在没有synchronized块的情况下编写多线程代码(这段代码是从书中复制的,可以从Apress site下载,我不打算在这里违反任何法律):

import java.util.concurrent.atomic.{AtomicReference => AtomR, AtomicLong}
import java.util.Random
import scala.collection.immutable.TreeHashMap

object Multics {
  type MT = Map[String, Int]

  val info: AtomR[MT] = new AtomR(TreeHashMap.empty)
  val clashCnt = new AtomicLong

  def main(argv: Array[String]) {
     runThread {
      repeatEvery(1000) {
        println("Clash Count: "+clashCnt+" Total: "+
                info.get.foldLeft(0)(_ + _._2))
      } }

    for (i  old + (name -> 0)}
      repeatEvery(ran.nextInt(100)) {
        doSet(info){old => old + (name -> (old(name) + 1))}
        cnt = cnt + 1
        if (cnt != info.get()(name))
          throw new Exception("Thread: "+name+" failed")
      } }
  }

  def runThread(f: => Unit) =
  (new Thread(new Runnable {def run(): Unit = f})).start

  def doSet[T](atom: AtomR[T])(update: T => T) {
    val old = atom.get
    if (atom.compareAndSet(old, update(old))) ()
    else {
      clashCnt.incrementAndGet
      doSet(atom)(update)
    }
  }

  def repeatEvery(len: => Int)(body: => Unit): Unit = {
    try {
      while(true) {

        Thread.sleep(len)
        body
      }
    } catch {
      case e => e.printStackTrace; System.exit(1)
    }
  }
}

根据我的理解,函数 doSet 中存在潜在的饥饿问题(不幸的线程可能总是冲突并导致StackOverflowException) . 我是对的,如果是的话,如何改进此代码来解决这个问题?

2 回答

  • 0

    首先,我认为你从书中的例子中粘贴的代码很缺乏;这让你很难理解你的问题 .

    其次,doSet()可能会被递归多次调用,但在这种情况下不会发生StackOverflowException,因为有一个保存宽限:尾递归 . doSet()在函数末尾递归调用(在递归调用之后不再进行处理)并且不能被覆盖(在对象内定义),这使得它被优化为循环而没有任何堆栈开销 .

    在2.8.0中,有一个名为@scala.annotation.tailrec的注释,当在def上使用时,它将确保def内的递归调用确实是尾递归的 .

  • 2

    看起来它使用Compare和Swap(http://en.wikipedia.org/wiki/Compare-and-swap)而不是锁定 .

    这种方法的一般想法是循环,直到您设法一致地设置值 .

    如果有饥饿问题,我不知道这个问题 . 我的猜测是理论上存在饥饿问题,但在实践中它会很好 .

相关问题