5Seven Concurrency Models in Seven Weeks

Erlang的Actor模型

消息传递

Actor消息传递监督树Let it crash

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第5章:Erlang的Actor模型(Actor Model)

一、导读

1.1 本章学习目标

第5章介绍 Actor 模型——Erlang 的并发基石。在 Actor 模型中,每个 Actor 是一个独立的计算单元,通过消息传递进行通信,不共享任何状态。

本章的核心学习目标包括:

  • 理解 Actor 模型的核心概念
  • 掌握消息传递的异步特性
  • 理解监督树和容错机制
  • 学会用 Actor 构建容错系统

1.2 为什么本章如此重要

Actor 模型被 Go、Elixir、Akka、Orleans 等众多框架采用。它的核心理念——"共享 nothing,通过消息通信"——是构建分布式系统的基础。Erlang 用 Actor 模型构建了电信级的99.999%可用性系统。

1.3 核心问题引导

Actor 模型和线程+锁模型有什么本质区别?

消息传递为什么比共享内存更安全?

监督树是如何实现容错的?

Actor 模型适合什么类型的应用?


二、核心概念详解

2.1 Actor 基础

erlang
%% 创建 Actor
-module(counter).
-export([start/0, loop/1]).

start() -> spawn(fun() -> loop(0) end).

loop(Count) ->
    receive
        {increment, From} ->
            NewCount = Count + 1,
            From ! {ok, NewCount},
            loop(NewCount);
        {get, From} ->
            From ! {ok, Count},
            loop(Count);
        stop ->
            ok
    end.

%% 使用
Pid = counter:start(),
Pid ! {increment, self()},
receive {ok, Count} -> io:format("Count: ~p~n", [Count]) end.

2.2 消息传递

erlang
%% 异步消息传递:发送后立即返回
Pid ! {message, data},

%% 同步等待回复
Rpc = fun(Pid, Msg) ->
    Pid ! {self(), Msg},
    receive
        {Pid, Reply} -> Reply
    after 5000 ->
        {error, timeout}
    end
end.

2.3 监督树

erlang
-module(supervisor).
-behaviour(supervisor).

init(_) ->
    {ok, {
        {one_for_one, 5, 60},
        [
            {worker1, {worker, start_link, []},
             permanent, 5000, worker, [worker]},
            {worker2, {worker, start_link, []},
             permanent, 5000, worker, [worker]}
        ]
    }}.

%% 重启策略:
%% one_for_one: 只重启崩溃的进程
%% one_for_all: 所有进程都重启
%% rest_for_one: 崩溃进程及其后面的进程重启

三、实战案例

3.1 案例一:简单的聊天室

erlang
-module(chat_room).
-export([start/0, join/2, send/2, leave/2]).

start() ->
    register(chat_room, spawn(fun() -> loop([]) end)).

join(Room, Name) ->
    Room ! {join, self(), Name},
    ok.

send(Room, Message) ->
    Room ! {send, self(), Message},
    ok.

leave(Room, Name) ->
    Room ! {leave, self(), Name},
    ok.

loop(Members) ->
    receive
        {join, Pid, Name} ->
            io:format("~s joined~n", [Name]),
            loop([{Pid, Name} | Members]);
        {send, From, Message} ->
            FromName = proplists:get_value(From, Members),
            lists:foreach(
                fun({Pid, Name}) ->
                    case Pid =:= From of
                        true -> ok;
                        false -> Pid ! {message, FromName, Message}
                    end
                end,
                Members
            ),
            loop(Members);
        {leave, Pid, Name} ->
            io:format("~s left~n", [Name]),
            loop(proplists:delete(Pid, Members))
    end.

四、本章小结

Actor 模型:独立计算单元,通过消息传递通信。

无共享:Actor 之间不共享状态,消除数据竞争。

监督树:层次化的容错机制,自动重启崩溃的进程。

Let it crash:不预防错误,让系统自动恢复。


五、思考与练习

Actor 模型的消息传递和 RPC 有什么区别?

监督树的"one_for_one"和"one_for_all"策略分别适用于什么场景?

用 Actor 模型设计一个简单的分布式任务调度系统。


5/7Erla
0%