scala / akka:保护数据免受竞争条件的影响

时间:2016-04-06 21:12:42

标签: scala akka

我是akka的新手。 任何人都可以建议/确认下一个代码/方法在重载下的多线程环境中是否安全? 与Execute方法有关的问题。 也很高兴知道一般/典型的方法。 PS。我确实试过检查下一篇文章:https://twitter.github.io/scala_school/concurrency.html 但这不是Akka特有的等。 谢谢,

以下是代码:


    TypeA   A001         A002         A003        A004
    ------- ------------ ------------ ----------- --------------------
    AAA001  EXT          mail         190.000000  example@example.com

2 个答案:

答案 0 :(得分:3)

正如所写,由于ExchangeSystem的状态是(有效的)全局变量,这对于竞争条件已经成熟。

不是拥有一个带有可变集合的全局对象,而是将一个状态隔离在一个actor中(这是演员的主要目的之一)。

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函数与调用但未在样本中定义的其他函数一起移动到actor中。

您还可以将它们移动到处理业务逻辑的ExchangeSystem类中,并在actor中编写它:

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

}

现在你已经消除了竞争条件,因为所有操作都是通过传递给actor的消息进行的。您还简化了测试,因为您的业务逻辑可以独立于actor本身进行测试 - 您的测试可以简单地实例化自己的ExchangeSystem,从而避免强制测试异步的复杂性。

答案 1 :(得分:2)

只是澄清一下。 @Ryan给出了关于通过在Actor中封装状态来降低可见性的好建议,但他的第一个声明是:

  

由于ExchangeSystem的状态,这对于竞争条件已经成熟   (有效地)全局变量。

给出错误的印象,即竞争条件存在

实际上,正如所写,根据几个条件,您的代码可能安全,也可能不安全。

  1. 如果您只访问Execute方法并且不直接触摸ListBuffer,那么您安全,因为此方法已同步。

  2. 如果您仅在 Execute演员的情况下访问ListBuffer而未触及ExchangeSystem字段,则非常安全即使没有明确的同步(实际上,在这种情况下,同步也是不好的做法)。

  3. 唯一可能违反线程安全的情况是,如果您尝试直接从不同的线程/ Actors修改ListBuffer字段,这是可能的,因为它们是公开的(但您还没有证明您是去做)。那是@Ryan的观点。

    另请注意,使用Actors时应避免明确同步,请阅读@ Ryan的建议,了解如何更改代码以使其更适合演员。