6

有人告诉我,(Scala)Actor 从来没有真正同时执行两个操作,这表明 act(或反应?或接收?)方法本质上是同步的。我知道 act 方法中的长时间操作会导致阻塞问题,并且我假设必须以某种方式同步对消息队列的访问......但是......

建议的是,接收消息告诉它增加内部计数器的参与者将以线程安全的方式增加计数器。不会同时处理两条更新消息,因此没有两条消息可以尝试同时更新计数器。

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

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

如果不是,那么在不需要一些同步/易失变量的情况下,以线程安全的方式对消息进行计数的适当惯用方法是什么?

4

3 回答 3

8

Actor 模型可用于将可变状态与外界隔离。当您有一个可变状态(例如,分配给多个并发进程的全局 ID 注册表)时,您可以将该可变状态包装在一个 Actor 中,并让客户端通过消息传递与 Actor 通信。这样,只有参与者直接访问可变状态,正如您所说,客户端消息排队等待读取和处理。消息不可变很重要。

为避免队列变满,消息处理(reactreceive等)尽可能短很重要。长时间运行的任务应该交给其他参与者:

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)回给 A,A 转发给 S
  • A 保留一个映射ActorRef C => (Sender S, Message M) ,因此如果它看到 C 失败,它可以使用新的 Actor 重试处理 M。

所以回顾一下,Actor 是多线程的,以至于多个客户端可以从不同的线程向它发送多条消息,并且保证 Actor 将串行处理所有这些消息(尽管排序可以受到各种非过度严格的约束)。

请注意,虽然 Actor 的react代码可以在多个线程上执行,但在单个给定时间点,它仅在单个给定线程上执行(您可以想象,Actor 在调度程序认为合适的情况下从一个线程跳到另一个线程,但这是技术细节)。注意:内部状态仍然不需要同步,因为 Actor保证处理消息之间的发生前语义。

并行性是通过让多个 Actor 并行工作来实现的,通常形成主管层次结构平衡工作负载

请注意,如果您只需要并发/异步计算,但您没有或无法摆脱全局状态,Future那么 s 是一个更好的组合和更简单的概念。

于 2012-09-13T05:50:37.000 回答
6

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

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

于 2012-09-13T04:56:38.877 回答
2

您可以使用单独的演员进行计数。当演员收到消息时,它可以向(单例)计数演员发送消息。这样你就可以有多个工人演员并且仍然计算消息。

Akka 有一个叫做的东西Agents,在这种情况下可能很有用。

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

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

于 2012-09-13T05:12:37.067 回答