Actor模型如何解决共享状态问题?求Erlang/Elixir取款示例
Actor模型下Erlang/Elixir规避共享状态的取款实现
Actor模型规避共享状态的核心逻辑是:让单个进程作为状态的唯一持有者,所有存取款操作都以消息形式与该进程交互,状态仅在进程内部更新,绝不对外暴露可被多进程直接访问的共享存储(比如ETS)。以下是具体实现示例:
Erlang 实现示例
-module(bank_account). -export([start/1, withdraw/2, deposit/2, get_balance/1]). % 启动账户进程,初始化余额 start(InitialBalance) -> spawn(fun() -> account_loop(InitialBalance) end). % 发送取款请求并等待结果 withdraw(AccountPid, Amount) -> AccountPid ! {withdraw, self(), Amount}, receive {result, Result} -> Result end. % 发送存款请求并等待结果 deposit(AccountPid, Amount) -> AccountPid ! {deposit, self(), Amount}, receive {result, Result} -> Result end. % 查询当前余额 get_balance(AccountPid) -> AccountPid ! {get_balance, self()}, receive {balance, Balance} -> Balance end. % 进程内部循环,维护余额状态(仅该进程能修改) account_loop(Balance) -> receive {withdraw, From, Amount} when Amount > 0, Balance >= Amount -> NewBalance = Balance - Amount, From ! {result, {ok, NewBalance}}, account_loop(NewBalance); {withdraw, From, _Amount} -> From ! {result, {error, insufficient_funds}}, account_loop(Balance); {deposit, From, Amount} when Amount > 0 -> NewBalance = Balance + Amount, From ! {result, {ok, NewBalance}}, account_loop(NewBalance); {deposit, From, _Amount} -> From ! {result, {error, invalid_amount}}, account_loop(Balance); {get_balance, From} -> From ! {balance, Balance}, account_loop(Balance) end.
关键说明
- 余额
Balance仅存在于account_loop/1的进程栈中,没有任何全局共享存储,只有该进程能修改状态。 - 所有外部操作都通过消息传递完成,进程处理请求后回复结果,状态更新后进入下一次循环,天然保证操作的原子性,不会出现多进程竞争问题。
Elixir 实现示例
基于Erlang VM,逻辑一致,语法更贴近函数式风格:
defmodule BankAccount do def start(initial_balance) do spawn(fn -> loop(initial_balance) end) end def withdraw(account_pid, amount) do send(account_pid, {:withdraw, self(), amount}) receive do {:result, result} -> result end end def deposit(account_pid, amount) do send(account_pid, {:deposit, self(), amount}) receive do {:result, result} -> result end end def get_balance(account_pid) do send(account_pid, {:get_balance, self()}) receive do {:balance, balance} -> balance end end defp loop(balance) do receive do {:withdraw, from, amount} when amount > 0 and balance >= amount -> new_balance = balance - amount send(from, {:result, {:ok, new_balance}}) loop(new_balance) {:withdraw, from, _amount} -> send(from, {:result, {:error, :insufficient_funds}}) loop(balance) {:deposit, from, amount} when amount > 0 -> new_balance = balance + amount send(from, {:result, {:ok, new_balance}}) loop(new_balance) {:deposit, from, _amount} -> send(from, {:result, {:error, :invalid_amount}}) loop(balance) {:get_balance, from} -> send(from, {:balance, balance}) loop(balance) end end end
补充说明
你提到的「将余额编码到消息中传递」存在逻辑误区:如果让多个进程持有余额副本,反而会引发状态不一致问题。Actor模型规避共享状态的正确方式是让单个进程垄断状态所有权,所有操作都通过与该进程通信完成,从根源上避免了共享状态的竞争。
内容的提问来源于stack exchange,提问作者wang kai
相关产品推荐
相关产品推荐

