2016-10-18 4 views
0

Примечание: Проблема, которую я решаю, имеет только образовательные цели, я знаю, что абстракция, которую я хочу создать, подвержена ошибкам и т. Д. ... Мне не нужно быстрое решение , Мне нужно объяснение.SyncVar производитель/потребительские потоки в scala

В книге я читал есть упражнение, которое говорит, что мне нужно реализовать SyncVar, которая имеет следующий интерфейс:

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

Мой комментарий: Хорошо кажется понятным, нужно немного синхронизации переменной, что я могу положите.

Объект SyncVar используется для обмена значениями между двумя или более потоками. При создании объекта SyncVar пуст:

° Вызов GET бросает исключение

° Вызов положить добавляет значение объекта SyncVar После значение добавляется к объекту SyncVar, можно сказать, что это непусто:

° Вызов прибудет возвращает текущее значение, и изменяет состояние опустошить

° Вызов положить Выдает исключение

Мои мысли: Это переменная, которая выдает исключение при пустом значении при вызове get или put, когда у нас есть значение, когда мы вызываем get, оно очищает предыдущее значение. Похоже, мне нужно использовать опцию.

Так я обеспечиваю следующую реализацию:

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 
    } 

Мой комментарий: Синхронно, ссылающегося на месте и получить, также имеют IsEmpty и непустые

Следующая задача заставляет меня путать: Объект SyncVar из предыдущего упражнения может быть громоздким для использования, из-за исключений, когда объект SyncVar находится в недопустимом состоянии. Внесите пару методов isEmpty и nonEmpty в объект SyncVar. Затем реализует поток производителя, который переносит диапазон чисел 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 (в правильном порядке). Не могли бы вы объяснить мне, что не так с моим решением?

ответ

1

Во-первых, ваш synchronized в get слишком узкий. Он должен окружать весь метод, как в put (можете ли вы подумать, почему?).

После фиксации, рассмотрим следующий сценарий:

  1. producerThread ставит 0 в syncVar.

  2. producerThread продолжает работать и пытается 1. syncVar.isEmpty возвращается false, чтобы он не поставил 1. Он продолжает петлю со следующим i вместо.

  3. consumerThread получает 0.

  4. producerThread ставит 2.

т.д. Так consumerThread никогда не может получить и напечатать 1, потому что producerThread никогда не ставит его там.

Подумайте, что делать producerThread, если syncVar не является пустым и что должно быть consumerThread, если оно есть.

+0

Да, вы правы, проблема заключается в том, что я не могу гарантировать, что 'get' будет называться по' ​​'consumerThread' после put', даже когда я применяю синхронизации на 'put' и' get'. Как я могу гарантировать, что после того, как потребительский поток 'put' вызовет' get' и установит значение 'None? –

+1

Подсказка: вам нужно _wait_ в 'producerThread', пока' syncVar' не будет пустым (и наоборот в 'consumerThread'). Как вы можете использовать внутренний цикл для этого? –

+0

Спасибо за подсказки, вместо того, чтобы решить это для меня :) –

1

Благодаря @Alexey Романов, я, наконец, реализовать способ передачи:

Объяснение:

Идея заключается в том, что производитель нить проверяет это syncVar пуст, если он ставит его, в противном случае ждет с while(syncVar.nonEmpty){} (используя занятое ожидание, что является плохой практикой, но важно знать об этом в образовательной цели), и когда мы покидаем цикл (остановим ожидание), мы помещаем переменную и оставляем цикл for для i == 0. Между тем потребительский поток занят в ожидании навсегда, и читает переменную, когда она не является частью.

Решение:

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() 
     } 
     } 
    } 

    } 
Смежные вопросы