2016-10-18 22 views
0

注意:我解決的問題只有教育目的,我知道我想創建的抽象是容易出錯等,我不需要快速解決方案,我需要解釋。SyncVar傳輸生產者/消費者線程在斯卡拉

在這本書我讀有運動,指出我需要實現SyncVar它具有以下接口:

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

我的評論:好吧,似乎可以理解的,需要一定的同步變化,我可以放或得。

SyncVar對象用於在兩個或多個線程之間交換值。 在創建時,SyncVar對象爲空:

°調用get拋出一個異常

°調用看跌增加了價值,SyncVar對象 值添加到SyncVar對象後,我們可以說,它非空:

°調用get返回當前值,並把狀態改變爲空

°調用放拋出一個異常

我的想法:這是一個變量,它在調用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和非空

下一個任務讓我困惑: 的由於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(按照正確的順序)。你能解釋我的解決方案有什麼問題嗎?

回答

1

首先,您的​​在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'後'被調用,甚至當我申請同步放''和'get'。我怎樣才能確保'put'調用消費者線程後肯定會調用'get'並將值設置爲'None? –

+1

提示:您需要在'producerThread'中等待_wait_,直到'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() 
     } 
     } 
    } 

    }