演员与线程的定义有何区别?

7

我试图理解演员(Actors)和线程(Threads)的定义差异。一些文章称演员是轻量级线程,而其他文章则指出演员不是线程。我也知道一个线程可以运行多个演员。我对如何准确区分演员和线程感到困惑。请帮助我理解,谢谢!

4个回答

22

ActorThread的抽象级别更高。

Thread是JVM的概念,而Actor是在JVM中运行的普通Java类,因此问题不是Actor vs Thread,而更多地关于Actor如何使用Threads

什么是Actor?

简单来说,Actor是一个实体,只接收一条消息,并对这些消息做出反应。

enter image description here

Actor如何使用Threads?

Actor接收到一条message时,它会执行一些actionAction代码如何在JVM中运行?如果您简化情况,可以想象Action在当前线程上执行action任务,也可能是Actor决定在线程池上执行action任务。无论怎样,只要确保每次只处理一条消息即可。

enter image description here


11
我试图理解演员(Actors)与线程(Threads)定义上的区别。其实它们并没有太多关联,所以很难谈论它们的差异。这就像谈论丰田凯美瑞和蓝色之间的区别。
演员是一个自包含的、封装的实体,通过发送消息与其他自包含、封装的实体进行通信。如果这听起来 正好 像对象的定义,那么你是正确的!演员模型是由卡尔·休伊特(Carl Hewitt)构思的计算模型,部分基于早期Smalltalks(Smalltalk-71,Smalltalk-72)的消息定向执行。而艾伦·凯在引用PLANNER'目标导向执行对Smalltalk的消息导向执行的影响时,也提到了卡尔·休伊特。 (PLANNER也是Prolog的前身,Erlang又是基于Prolog的,最初是在Prolog中编写的库,并且最初的实现也是在Prolog中编写的。) 此外,卡尔·休伊特和艾伦·凯都以文特·瑟夫(Vint Cerf)和鲍勃·康(Bob Kahn)在后来成为互联网的早期工作为启示,并且两者都深受自然界的启发,卡尔·休伊特受物理学启发,而艾伦·凯则是受微生物学启发。因此,演员和对象之间存在深层次的联系和相似之处,并不令人意外。
演员和对象基本上是相同的东西。我们通常期望面向对象系统中的消息发送是同步、瞬时、可靠和有序的,而对于演员来说,没有这样的保证。这就是演员和对象之间的主要区别:演员是对象,其中的消息可能会丢失、重新排序、花费很长时间,并且是异步的。(唯一的保证是消息最多被传递 一次。)此外,接收者通常没有对发送者的隐式引用,因此如果接收者想要回复消息,则发送者需要随消息一起传递自己的引用(与典型的面向对象系统不同,在那里我总是可以为发送者返回结果)。一个Actor只做三件事情,恰好是这三件事情,不多也不少:
  1. 创建新的Actors
  2. 发送消息给它已知道的Actor地址(即:它自己创建的Actor地址和在消息中被发送过来的Actor地址)
  3. 指定如何处理下一条消息(这实质上就是如何建模可变状态)
注意,要向一个Actor发送消息,你需要有该Actor的地址。你只能向地址发送消息。一个Actor可能有多个地址,而且多个Actor可能隐藏在单个地址后面。地址是无法伪造的,你不能自己构造一个地址,必须由他人提供。(这与C语言中的指针有很大的不同,更像Java中的对象引用。)
在Microsoft的Channel9社区网站上,有一段非常好的白板讲解视频,主要是Carl Hewitt和Erik Meijer之间的对话,讲述了Actor模型:Hewitt、Meijer和Szyperski:Actor模型(你想知道的一切,但不敢问)。视频以易于理解的方式介绍了Actor模型的基本思想以及Actor公理的基本概念。
Carl Hewitt还被邀请在斯坦福大学的EE380计算机系统研讨会上发表演讲,他谈到了如何为一致性韧性编写多核程序,包括介绍Actors的内容:如何为不一致性韧性编写多核程序

相比之下,线程只是执行的串。它们本质上只有一个指令指针和一个调用堆栈。特别地,线程没有内存(自己的内存)。进程的所有线程共享同一块内存。这意味着,除其他外,单个行为不良的线程可以使进程中的所有线程崩溃,并且对该共享内存的所有访问都必须进行精细控制。

一些文章说演员是轻量级线程,而另一些文章则说明演员不是线程。我也明白,一个线程可以运行多个演员。

你混淆了Actor的概念和可能的实现方式。

在现有环境中实现Actor的一个非常简单的方法是将每个Actor作为进程。例如,这就是BEAM(Erlang的主要实现)的工作原理。这是可行的,因为在BEAM上,进程非常便宜且轻量级;一个进程仅重约300字节,在10年前的廉价32位单核笔记本电脑上可以有数千万个进程。但是,对于Windows进程等更昂贵的进程来说,这种方式是不可行的。

还有一种在JVM上实现Erlang的方法(称为Erjang),它使用了Kilim,在JVM上使用字节码重写实现极轻量级Actor。(请注意,Erjang和Kilim似乎都已经停止更新。)

如果想要在线程上实现Actor,则存在这样一个问题:演员完全是隔离的,而线程是完全共享的。因此,您需要以某种方式提供此隔离。

在诸如JVM之类的系统上实现Actor的一种常见方法是将Actor实现为对象,然后将其调度到线程池中以获得异步属性。这就是Akka的工作方式。


4
我强烈建议首先阅读官方文档: https://doc.akka.io/docs/akka/current/scala/general/actors.html 幕后,Akka将在实际线程集上运行一组演员,其中通常许多演员共享一个线程,并且一个演员的后续调用可能最终在不同的线程上处理。 Akka确保此实现细节不会影响处理演员状态的单线程性。

1

线程是一种“活动序列”,与特定对象无关。它可以执行属于任意对象的代码。

而演员则涉及到一个特定对象,该对象“拥有”自己的“活动序列”。但请注意,这个演员序列不一定是线程。相反!

从实现方面来看,可以使用一个线程驱动多个演员对象。


但是如果我使用线程池来实现Actor,而不是让每个Actor在自己的私有线程中运行呢?在这种情况下,没有任何一个Actor会“拥有”它自己的线程。 - Solomon Slow
整个想法是,一个演员不拥有一个线程。因为线程太重了。你想在你的系统中使用数百万个演员! - GhostCat
没错,但是你把线程定义为一系列活动,然后又说演员拥有自己的一系列活动。我只是试图以新手的方式阅读你的文本:一个新手可能会认为你说演员必须拥有自己的线程。 - Solomon Slow
我知道了。我已经相应地更新了答案。 - GhostCat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接