首页
Processes(进程)

Elixir 是面向并发的编程语言,基于 Erlang VM(BEAM)虚拟机。Elixir 能够非常简单地实现并发编程,它的并发模型是 actor 模型。

Spawn

创建一个新进程最简单的办法是使用spawn/1,它接受一个匿名函数或命名函数作为参数

spawn/1返回一个 PID(进程标识符)。生成的进程将执行给定的函数,并在函数完成后退出

iex> pid = spawn fn -> 1 + 2 end
#PID<0.44.0>
iex> Process.alive?(pid)
false

通过self/0命令检索当前进程的 PID

iex> self()
#PID<0.41.0>
iex> Process.alive?(self())
true

异步运行函数,可以用spawn/3

defmodule Example do
  def add(a, b) do
    IO.puts(a + b)
  end
end

iex> spawn(Example, :add, [2, 3])
5
#PID<0.80.0>

消息传递(send 和 receive)

我们可以通过send/2发送信息到进程,通过receive/1接收。

iex> send self(), {:hello, "world"}
{:hello, "world"}
iex> receive do
...>   {:hello, msg} -> msg
...>   {:world, msg} -> "won't match"
...> end
"world"

receive/1支持哨兵子句和case/2等子句。receive/1支持超时时间

iex> receive do
...>   {:hello, msg}  -> msg
...> after
...>   1_000 -> "nothing after 1s"
...> end
"nothing after 1s"

一个完整的例子

defmodule Example do
  def listen do
    receive do
      {:ok, "hello"} -> IO.puts("World")
    end

    listen()
  end
end

iex> pid = spawn(Example, :listen, [])
#PID<0.108.0>

iex> send pid, {:ok, "hello"}
World
{:ok, "hello"}

iex> send pid, :ok
:ok

spwan_link/1

当程序崩溃的时候,spwan就会有问题。父进程不知道子进程出错会导致程序异常。

def explode, do: exit(:kaboom)

iex> pid= spawn(Example, :explode, [])
#PID<0.143.0>
iex> pid = spawn_link(Example, :explode, [])
** (EXIT from #PID<0.102.0>) shell process exited with reason: :kaboom

Interactive Elixir (1.10.2) - press Ctrl+C to exit (type h() ENTER for help)

如果不希望链接的进程导致当前的进程跟着崩溃,可以使用Process.flag/2函数捕获进程的错误退出。当捕获到被链接的进程发生错误退出时(trap_exit 设为 true), 就会收到像 {:EXIT, from_pid, reason} 这样的三元组形式的退出信号。

defmodule Example do
  def explode, do: exit(:kaboom)

  def run do
    Process.flag(:trap_exit, true)
    spawn_link(Example, :explode, [])

    receive do
      {:EXIT, _from_pid, reason} -> IO.puts("Exit reason: #{reason}")
    end
  end
end

iex> Example.run
Exit reason: kaboom
:ok

spawn/1spawn_link/1是用于在 Elixir 中创建进程的基本原型。

尽管到目前为止我们已经完全使用它们,但大部分时间我们都会使用基于它们的抽象。让我们看看最常见的一个,称为任务。

任务(Tasks)

任务建立在 spawn 函数之上,以提供更好的错误报告和自省。

使用Task.start/1Task.start_link/1来替代spawn/1spawn_link/1。它们会返回{:ok,pid},而不仅仅是 PID。此外,Task提供了方便的功能,如Task.async/1Task.await/1

参考:
https://github.com/KINGMJ/elixir-learning/blob/elixir-tutorial/lib/demo7.ex