在 Elixir 中,所有代码都在进程内运行。Elixir 的进程:
默认情况下,函数会在调用它的那个进程中执行。当你需要在新进程中显式运行某个函数时,可以用 spawn:
spawn/1 接受一个函数并直接执行它。
spawn(fn -> 2 + 2 end)
# => #PID<0.125.0>
spawn/3 通过模块名、函数名(以原子形式表示)以及要传给该函数的实参列表来执行函数。
spawn(String, :split, ["hello there", " "])
# => #PID<0.113.0>
函数执行完毕后,进程会立即退出。
你可以用 Process.alive?/1 检查进程是否仍然_存活_(正在执行):
pid = spawn(fn -> 2 + 2 end)
Process.alive?(pid)
# => false
进程之间不直接共享信息。进程通过_发送消息_来共享数据。这种并发模式叫做 Actor 模型。
用 send/2 给进程发送消息。
send(pid, :hello)
send 不会检查消息是否被接收,也不会检查接收者是否仍然存活。消息可以是任意类型。
你可以用 receive/1 接收发送给当前进程的消息。
receive 会一直等待,直到进程邮箱中出现一条匹配任一给定模式的消息。
after 块设置超时。receive/1 中写一个兜底的 _ 分支,以免未读取的消息越积越多,最终耗尽内存。receive do
{:ping, sender_pid} -> send(sender_pid, :pong)
_ -> nil
after
5000 ->
{:error, "No message in 5 seconds"}
end
如果你想接收多条消息,就需要递归地调用 receive/1。常见做法是实现一个递归函数,比如命名为 loop,它调用 receive/1,对消息做一些处理,然后调用自身来等待更多消息。如果你需要在多次 receive/1 调用之间携带一些状态,可以把状态作为实参传给这个 loop 函数。
def loop(state) do
receive do
:increment_by_one ->
loop(state + 1)
{:report_state, sender_pid} ->
send(sender_pid, state)
loop(state)
:stop ->
nil
_ ->
loop(state)
end
end
实践中很少直接使用这种方式。Elixir 提供了并发抽象,比如 Agent 模块 或 GenServer 行为,它们都建立在接收循环之上。不过,要想高效地使用这些抽象,理解这些基础知识至关重要。