2016-04-06 58 views
0

我是新來的阿卡。 任何人都可以建議/確認在重負載下多線程環境下的代碼/方法將會安全嗎? 與Execute方法特別相關的問題。 也將很高興知道這個一般/典型的方法。 PS。我也嘗試檢查下一篇文章:https://twitter.github.io/scala_school/concurrency.html 但這不是阿卡具體等 感謝,scala/akka:保護數據免於競爭條件

下面是代碼:

object ExchangeSystem { 

    val openOrders = new ListBuffer[Order]() 
    val executedOrders = new ListBuffer[Order]() 

    def Execute(matchedOrder: Order, order: Order) : Unit = 
    { 
    this.synchronized { 

     order.SetAsExecuted(order.price) 
     matchedOrder.SetAsExecuted(order.price) 

     openOrders -= matchedOrder 

     executedOrders += order 
     executedOrders += matchedOrder 
     } 
    } 


class ExchangeSystem extends Actor { 
    import ExchangeSystem._ 

    def receive = { 
    case order : Order => { 

     ExchangeSystem.message = order 

     // Attempt to match order 
     val matchedOrder = ExchangeSystem.matchOrder(order) 

     if (matchedOrder != None){ 
      ExchangeSystem.Execute(matchedOrder.get, order) 
     } 
     else { 
     openOrders += order 
     } 
    } 
    } 


} 

回答

3

書面,這是成熟的競爭條件,由於ExchangeSystem的狀態是(有效)全局變量。

與具有可變集合的全局對象不同,將該狀態與演員分開(這是演員的主要目的之一)。

class ExchangeSystem extends Actor { 

    var openOrders = List.empty[Order] 
    var executedOrders = List.empty[Order] 

    def receive = { 
    case order : Order => 

     // Attempt to match order 
     val matchedOrder = ExchangeSystem.matchOrder(order) 

     matchedOrder match { 
     case Some(matched) => execute(matched, order) 
     case None => openOrders += order 
     } 

    } 

} 

ExchangeSystem.Execute功能也應移入演員,與所謂的其他功能,但不是你的樣品中定義一起。

你也可以移動這些成處理你的業務邏輯,組成它的演員內的ExchangeSystem類:

case class ExchangeSystem(openOrders: List[Order], executedOrders: List[Order]) { 

    def execute(matched: Order, order: Order): ExchangeSystem = { 
    order.SetAsExecuted(order.price) 
    matchedOrder.SetAsExecuted(order.price) 

    this.copy(
     openOrders = openOrders - matchedOrder, 
     executedOrders = executedOrders + order + matchedOrder 
    ) 
    } 

    def matchOrder(order: Order): Option[Order] = ??? 
    def withOpenOrder(order: Order): ExchangeSystem = this.copy(openOrders = openOrders + order) 
} 

注意這個對象如何是不變的,它永遠不會改變自己的狀態,而不是返回新ExchangeSystem具有更新狀態的實例。然後你就可以將其封裝在一個演員像這樣:

class ExchangeSystemActor extends Actor { 

    var exchangeSystem = ExchangeSystem(Nil, Nil) 

    def receive = { 
    case order : Order => 
     // Attempt to match order 
     val matchedOrder = exchangeSystem.matchOrder(order) 

     matchedOrder match { 
     case Some(matched) => exchangeSystem = exchangeSystem.execute(matched, order) 
     case None => exchangeSystem = exchangeSystem.withOpenOrder(order) 
     } 
    } 

} 

現在你已經消除了競爭條件,因爲所有的操作都是通過傳遞給演員的消息發生。您還簡化了測試,因爲您的業務邏輯可以獨立於角色本身進行測試 - 您的測試只需實例化自己的ExchangeSystem,避免了強制測試異步的複雜性。

2

只是爲了澄清。 @Ryan了關於由演員裏面封裝狀態降低能見度良好的建議,但他首先聲明:

這是成熟的競爭條件,由於ExchangeSystem的狀態 (有效)全局變量。

給出的錯誤印象是種族條件存在

實際上,正如書面所述,根據幾種情況,您的代碼可能安全也可能不安全。

  1. 如果您正在訪問只有Execute方法,而不要接觸ListBuffer小號直接,那麼你安全,因爲此方法是同步的。

  2. 如果你不只是從ExchangeSystem演員觸摸ListBuffer場訪問Execute,你是絕對安全即使沒有明確的同步(實際上,同步在這種情況下不好的做法)。

可能違反此線程安全的唯一情況是,如果你想直接從不同的線程/演員,這是可能的,因爲他們是公衆修改ListBuffer領域(但你沒有證明你這樣做)。這是@瑞恩的觀點。

另外請注意,使用演員時,你應該避免顯式同步,請閱讀@ Ryan的建議如何改變你的代碼,使其更適合演員。

+0

謝謝,保持一個演員的數據是我的一般感受。我認爲第二種解決方案會爲我做。 – Pavel