在 Elixir 中,所有程式碼都在行程裡執行。Elixir 的行程:
預設情況下,函式會在被呼叫的同一個行程中執行。當你需要明確地在新行程中執行某個函式時,可以使用 spawn:
spawn/1 接受一個函式,並直接執行它。
spawn(fn -> 2 + 2 end)
# => #PID<0.125.0>
spawn/3 接受一個函式,並透過模組名稱、函式名稱(以 atom 表示)以及要傳給該函式的引數陣列來執行它。
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 行為,兩者都建立在接收迴圈之上。不過,要能有效率地使用這些抽象機制,理解這些基礎概念至關重要。