2012-09-13 54 views
6

演员的理解threadedness我已经告诉(斯卡拉)演员从来没有真正在同一时间执行两个操作,这表明该行为(或反应?或接受?)方法本质上是同步的。我知道在行为方式长时间操作可能导致阻塞的问题,我认为访问消息队列必须以某种方式进行同步......但是在斯卡拉

什么建议是一个演员接收消息告诉它增加一个内部计数器会以线程安全的方式增加计数器。没有两个更新消息会被同时处理,因此没有两个消息可能会尝试同时更新计数器。

在一个演员的计数器属性听起来像“共享状态”。

这样的操作确实是完全线程安全的吗?如果是这样,一个演员如何以一种有效的方式使用多个核心机器?一个演员如何多线程?

如果没有,什么是适当的习惯的方法,而不需要一些同步/ volatile变量来计算在线程方式的消息?

回答

8

角色模型可以被用来从外界隔离的可变状态。当你有一个可变状态(例如一个ID分配给多个并发进程的全局注册表)时,你可以在一个Actor中包装这个可变状态,并使客户端通过消息传递与Actor通信。这样,只有演员直接访问可变状态,并且如您所说,客户端消息排队等待逐个读取和处理。消息是不可改变的,这一点很重要。

为了避免队列变满,重要的是消息处理(react,receive等)尽可能短。长时间运行的任务应移交给其他演员:

1. Actor A receives a message M from sender S 
2. A spawns a new actor C 
3. A sends (S, f(M)) to C 
4. In parallel: 
4a. A starts processing the next message. 
4b. C does the long-running or dangerous (IO) task, 
    When finished, sends the result to S, 
    and C terminates. 

在这个过程中的一些替代方案:

  • C发送(S, result)回阿谁转发至S
  • A保持映射ActorRef C => (Sender S, Message M)so in case it sees C fail,它可以用新的Actor重试处理M.

所以回顾一下,一个Actor是多线程的,多个客户端可以从多个线程发送多条消息,并且保证Actor将连续处理所有这些消息(尽管排序可以是受各种非过分严格的限制)。

注意的是,虽然演员的react代码may be executed on various threads,在单个时间给定点它是一个单定线程上执行只(你能想象这样的演员从线程跳转到一个线程调度器认为合适,但是这是一个技术细节)。 注意:内部状态仍然不需要syncronization,因为处理消息之间的演员guarantee happens-before semantics

并行性是通过使多个Actor并行工作来实现的,通常形成supervisor hierarchiesbalancing workload

注意,如果你所需要的是并发/异步计算,但是你没有或者可以摆脱全局状态,Futures are a better composing和更容易的概念。

+0

这是一个很好的解释,谢谢! – Brian

+0

很好的答案。我会补充说,虽然一个参与者的反应可以运行在不同的线程上(因此“通常”会受到内存访问问题的影响),但实际上并不需要同步访问参与者的状态。这是因为演员框架在执行演员之前已经这样做了。请参阅http://stackoverflow.com/questions/1031167/should-my-scala-actors-properties-be-marked-volatile –

+0

很高兴它有帮助。 Régis:谢谢,更新文本更加精确。 – ron

6

“一个演员”不是多线程的,而是一个演员系统通常是。每个actor每次只执行一个动作,但当有多个actor时,每个actor可以并行操作其各自的封装状态。计数器属性不是共享可变状态,如果它不共享之间演员

如果你的问题是关于角色系统的实现,它有所不同,通常是可配置的,即默认的Scala角色可以配置为单线程或线程池运行或使用Java ForkJoin任务。我发现scala.actors的源代码非常易读,所以如果你想了解发生了什么,我建议看看。

+0

+1指向Scala源代码的指针。它很好地显示了这个概念。 – sourcedelica

2

你可以使用一个单独的actor来进行计数。当一个演员收到一条消息时,它可以向一个(单身)计数演员发出消息。通过这种方式,您可以拥有多个工作人员并仍然计数消息。

阿卡有一些所谓的Agents这可能是在这种情况下非常有用。

val counter = Agent(0) 
counter send (_ + 1) 

http://doc.akka.io/docs/akka/2.0.2/scala/agents.html

+0

代理是为这个用例量身定制的。就线程性和同步性而言,它们的工作方式类似于用于写入的actor和用于读取的AtomicX。 – sourcedelica