SyncVar在scala中传输生产者/消费者线程

时间:2016-10-18 07:52:10

标签: multithreading scala synchronization

注意:我解决的问题只有教育目的,我知道我想创建的抽象容易出错等等......我不需要快速解决方案,我需要解释

在我正在阅读的书中,有一个练习说我需要实现具有以下界面的SyncVar:

class SyncVar[T] {
  def get(): T = ???
  def put(x: T): Unit = ???
}

我的评论:好的似乎可以理解,需要一些我可以放置或获取的同步变量。

SyncVar对象用于在两个或多个线程之间交换值。 创建时,SyncVar对象为空:

°调用get抛出异常

°Calling put为SyncVar对象添加一个值 将值添加到SyncVar对象后,我们可以说它是非空的:

°Calling get返回当前值,并将状态更改为空

°Calling put抛出异常

我的想法:这是变量,在调用get时抛出空值异常,或者当我们有值时抛出,当我们调用get时它会清除以前的值。好像我需要使用Option。

所以我提供了以下实现:

class SyncVar[T] {
    var value: Option[T] = None
    def get(): T = value match {
      case Some(t) => this.synchronized {
        value = None
        t
      }
      case None => throw new IllegalArgumentException("error get")
    }
    def put(x: T): Unit = this.synchronized{
      value match {
        case Some(t) => throw new IllegalArgumentException("error put")
        case None => value = Some(x)
      }
    }
    def isEmpty = value.isEmpty
    def nonEmpty = value.nonEmpty
  }

我的评论 同时调用put和get,也有isEmpty和nonEmpty

下一个任务让我感到困惑: 上一练习中的SyncVar对象使用起来很麻烦, 由于SyncVar对象处于无效状态时的异常。实行 SyncVar对象上的一对方法isEmpty和nonEmpty。然后, 实现一个生产者线程,它将一系列数字0转移到15 到打印它们的消费者线程。

据我所知,我需要两个线程:

     //producer thread that produces numbers from 1 to 15
     val producerThread = thread{
       for (i <- 0 until 15){
         println(s"$i")
         if (syncVar.isEmpty) {
           println(s"put $i")
           syncVar.put(i)
         }
       }
      }

//consumer that prints value from 0 to 15
val consumerThread = thread{
  while (true) {
    if (syncVar.nonEmpty) println(s"get ${syncVar.get()}")
  }
}

问题: 但是这个代码是由非确定性引起的,所以每次都有不同的结果,而我需要打印1到15之间的数字(按照正确的顺序)。你能解释一下我的解决方案有什么问题吗?

2 个答案:

答案 0 :(得分:1)

首先,synchronized中的get太窄了。它应该围绕整个方法,例如put(你能想到为什么吗?)。

修复后,请考虑以下情况:

  1. producerThread将0放入syncVar

  2. producerThread继续运行并尝试放置1. syncVar.isEmpty返回false所以它不会放1.它继续循环下一个i代替。

  3. consumerThread获得0。

  4. producerThread放置2.

  5. 等。因此consumerThread永远无法获取并打印1,因为producerThread永远不会将其放在那里。

    如果producerThread不为空,请考虑syncVar应该执行的操作以及consumerThread应该执行的操作。

答案 1 :(得分:1)

感谢@Alexey Romanov,最后我实现了转移方法:

<强>解释

这个想法是,生成器线程检查是syncVar是空的,如果是它放置它,否则它等待while(syncVar.nonEmpty){}(使用忙等待,这是不好的做法,但它很重要在教育目的中了解它以及当我们离开循环(停止忙等待)时,我们放置变量并为i == 0留下for循环。同时消费者线程忙于等待永远,并且当它是非空时读取变量

<强>解决方案:

  def transfer() = {
    val syncVar = new SyncVar[Int]
    val producerThread = thread{
      log("producer thread started")
      for (i <- 0 until 15){
        if (syncVar.isEmpty) {
          syncVar.put(i)
        } else {
          while (syncVar.nonEmpty) {
            log("busy wating")
          }
          if (syncVar.isEmpty) {
            syncVar.put(i)
          }
        }

      }
    }

    val consumerThread = thread{
      log("consumer thread started")
      while (true) {
        if (syncVar.nonEmpty) {
          syncVar.get()
        }
      }
    }

  }
相关问题