Actor 基础:定义与启动
在 Monarch 中,Actor 是构建分布式应用的基本积木。它是一个运行在独立进程(甚至不同物理主机)中的 Python 对象:拥有私有状态、按顺序处理消息、参与监督树,并通过 endpoint 暴露远程调用接口。
1. 定义 Actor
定义 Actor 非常简单:继承 monarch.actor.Actor,用 @endpoint 装饰器标记允许远程调用的方法。
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。
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() 在单机上拉起一组进程,模拟真实的分布式环境。
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__ 在正常停止与错误终止时都会运行,适合关闭文件、网络连接等资源:
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 的方法并获取结果。
