Skip to content

Actor 基础:定义与启动 ​

在 Monarch 中,Actor 是构建分布式应用的基本积木。它是一个运行在独立进程(甚至不同物理主机)中的 Python 对象:拥有私有状态、按顺序处理消息、参与监督树,并通过 endpoint 暴露远程调用接口。

1. 定义 Actor ​

定义 Actor 非常简单:继承 monarch.actor.Actor,用 @endpoint 装饰器标记允许远程调用的方法。

python
from monarch.actor import Actor, endpoint

class Counter(Actor):
    def __init__(self, initial_value: int):
        # __init__ 在 Actor 构造时执行,参数由 spawn() 转发而来
        self.value = initial_value

    @endpoint
    def increment(self) -> None:
        self.value += 1

    @endpoint
    def get_value(self) -> int:
        return self.value

为什么用 @endpoint 而不是直接调用方法? 官方的解释是:这样设计能确保 IDE 对 Actor 调用提供正确类型的自动补全。没有被 @endpoint 标记的方法只能在 Actor 内部使用。

2. 在当前进程中启动 Actor ​

最简单的入门方式:用 this_proc() 在当前进程里启动一个 Actor。

python
from monarch.actor import this_proc

counter: Counter = this_proc().spawn("counter", Counter, initial_value=0)

this_proc() 返回当前进程的句柄,让你精确控制 Actor 运行的位置。Monarch 对"东西在哪运行"非常较真——同一进程里的两个 Actor 可以共享内存空间,同一主机的两个 Actor 可以通过 /dev/shm 通信。

⚠️ 注意:即使两个 Actor 在同一进程,它们之间传消息目前也总是需要序列化。

3. 在本机模拟多进程 ​

学习阶段最实用的姿势:用 this_host() 在单机上拉起一组进程,模拟真实的分布式环境。

python
from monarch.actor import this_host

# 在当前主机上启动 8 个进程,逻辑维度命名为 "gpus"
procs = this_host().spawn_procs(per_host={"gpus": 8})

# 在每个进程上各启动一个 Counter,initial_value 统一传 0
counters: Counter = procs.spawn("counters", Counter, 0)

此时 counters 不再是单个 Actor,而是一个 ActorMesh——8 个 Counter 实例组成的一维网格。spawn() 的多余参数会作为构造参数转发给每个 Actor 的 __init__。

4. Actor 的生命周期 ​

Actor 从创建到销毁经历四个阶段:

阶段触发说明
创建 (Creating)spawn()运行时分配资源、注册 Actor、创建邮箱(mailbox)、分配 Actor ID
构造 (Constructing)__init__()初始化全部状态;完成后 Actor 准备好接收消息
运行 (Running)收到消息逐条处理邮箱中的消息,一条处理完才处理下一条
终止 (Terminating)stop() 或未处理异常正常停止或错误终止,__cleanup__ 运行,失败向父级传播

清理钩子 __cleanup__ 在正常停止与错误终止时都会运行,适合关闭文件、网络连接等资源:

python
class FileWriter(Actor):
    def __init__(self, path: str):
        self.f = open(path, "w")

    @endpoint
    def write(self, line: str) -> None:
        self.f.write(line)

    def __cleanup__(self, exc: Exception | None) -> None:
        # 正常停止时 exc 为 None,错误终止时携带异常
        self.f.close()

注意:OOM、panic、致命信号(如 SIGSEGV)等致命失败不会触发 __cleanup__。

顺带一提:__cleanup__ 是单个 Actor 的收尾;当整个控制器程序用完 Monarch 后,还可以调用 monarch.actor.shutdown_context() 关停本进程的客户端做整体收尾(正常退出解释器时也会自动执行),细节见「网格操作」一章。

关键点 ​

  • Actor 是有状态的:self.value 保存在远程进程的内存中,消息处理是顺序的,Actor 内部状态天然一致;
  • @endpoint 是公开 API:只有被标记的方法才能远程调用,且支持 IDE 类型补全;
  • 位置是显式的:this_proc() / this_host() / 集群配置,三档粒度由你决定;
  • __init__ 的参数由 spawn() 转发,__cleanup__ 负责收尾。

接下来,我们将学习如何调用这些 Actor 的方法并获取结果。

Monarch中文教程