【问题标题】:Synchronize on a var in Scala在 Scala 中的 var 上同步
【发布时间】:2016-01-17 13:17:06
【问题描述】:

问题是同时在几页文本中搜索感叹号,一旦任何线程找到它,所有其他线程都应该停止搜索。

代码:

  object AntiVolatile {
    val pages = for (i <- 1 to 15) yield new Page("!Na" * rand.nextInt(1000) + " Batman!", -1)
    var found = Some(false)

    def run(): Unit = {
      for (p <- pages) yield thread {
        var i = 0
        var foundInThread = found.get
        while (i < p.txt.length && !foundInThread)
          if (p.txt(i) == '!') {
            found.synchronized {
              found match {
                case Some(true) => foundInThread = true
                case Some(false) => {
                  p.position = i
                  found = Some(true)
                  Thread.sleep(1)
                }
                case _ =>
              }
            }
          } else i += 1
        // if still not found, wait for another thread to find it.
        def wait(): Unit = {
          found match {
            case Some(false) => wait()
            case _ =>
          }
        }
        wait()
        log(s"results: ${pages.map(_.position)}")
      }
    }
  }

好像没问题:

Thread-29: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-27: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-28: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-26: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-30: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-31: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-32: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-25: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-33: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-34: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-39: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-38: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-37: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-36: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-35: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)

但后来我意识到found 不是一个常量实例,因为它稍后会重新分配给一个新的Option 对象。 (为什么代码实际上可以工作?)

所以我想出了一个解决办法:

  object AntiVolatile {
    case class Found(var isFound: Boolean)
    val pages = for (i <- 1 to 15) yield new Page("!Na" * rand.nextInt(1000) + " Batman!", -1)
    val found = Found(false)

    def run(): Unit = {
      for (p <- pages) yield thread {
        var i = 0
        var foundInThread = found.isFound
        while (i < p.txt.length && !foundInThread)
          if (p.txt(i) == '!') {
            found.synchronized {
              found match {
                case Found(true) => foundInThread = true
                case Found(false) => {
                  p.position = i
                  found.isFound = true
                  Thread.sleep(1)
                }
                case _ =>
              }
            }
          } else i += 1
        // if still not found, wait for another thread to find it.
        def wait(): Unit = {
          found match {
            case Found(false) => wait()
            case _ =>
          }
        }
        wait()
        log(s"results: ${pages.map(_.position)}")
      }
    }
  }

这两个版本的行为似乎相同,为什么?我希望在第一个版本中会出现一些错误。

github 仓库链接:https://github.com/kindlychung/learnConcurrentScala/blob/master/src/main/scala/org/learningconcurrency/ch2/Ch2.scala

【问题讨论】:

  • 这在任何一个版本中对我来说似乎都不是“好的”,因为结果集的大小不应该都是一样的,而且 !总是在第一位?可能我没有得到Page的内部结构
  • 另外,我不明白你为什么期望他们表现不同。所有线程都为foundInThread 创建一个值并对其进行迭代(不是found),直到完成各自的页面。同步仅在最后(因此无关紧要),您似乎没有为自己节省任何工作。另外,你正在忙着等待所有线程,这很糟糕:-(
  • 忙碌的等待纯粹是为了演示。请看我添加的github链接。

标签: scala concurrency


【解决方案1】:

您是否对学习并发感兴趣或者您是否正在解决实际问题并不完全清楚。话虽如此,我将假设您正在尝试解决问题。

为什么不使用期货?

import java.util.concurrent.TimeUnit

import scala.concurrent.duration.Duration
import scala.concurrent.{ExecutionContext, Await, Future}
import scala.util.Random
import ExecutionContext.Implicits.global

object Main extends App {

  case class Page(number: Int, text: String)

  val pages = for (i <- 1 to 15) yield Page(i, "!Na" * Random.nextInt(1000) + " Batman! ")

  val searchFutures = pages.map { p => Future {
    val position = p.text.indexOf("!")
    s"Exclamation mark found on page ${p.number} at position: $position"
  }}

  val firstCompleted = Future.firstCompletedOf(searchFutures)

  val result = Await.result(firstCompleted, Duration(5, TimeUnit.SECONDS))
  println(result)

}

【讨论】:

    猜你喜欢
    • 2018-09-08
    • 2014-05-31
    • 2013-10-10
    • 2013-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多