查看源代码 Process (Elixir v1.16.2)
用于处理进程和进程字典的便利功能。
除了此模块中提供的函数之外,Kernel
模块还公开并自动导入了一些与进程相关的基本功能,这些功能可以通过以下函数获得:
Kernel.spawn/1
和Kernel.spawn/3
Kernel.spawn_link/1
和Kernel.spawn_link/3
Kernel.spawn_monitor/1
和Kernel.spawn_monitor/3
Kernel.self/0
Kernel.send/2
虽然此模块提供了用于处理进程的低级便利功能,但开发人员通常使用抽象,例如 Agent
、GenServer
、Registry
、Supervisor
和 Task
来构建他们的系统,并且仅在收集信息、捕获退出、链接和监控时才使用此模块。
别名
别名是 Erlang/OTP 24 中引入的一项功能。别名是用来引用 PID 以便向其发送消息的一种方式。使用别名的优势在于,即使被别名引用的进程仍在运行,也可以将其停用。如果向停用的别名发送消息,则不会发生任何事情。这使得请求/响应场景更容易实现。
可以使用 alias/0
或 alias/1
设置别名,然后可以使用 send/2
向该别名发送消息,就像使用 PID 一样。要停用别名,可以使用 unalias/1
。如果向停用的别名发送消息,则不会发生任何事情。
例如,您可以创建一个进程来侦听 :ping
消息
def server do
receive do
{:ping, source_alias} ->
send(source_alias, :pong)
server()
end
end
现在,另一个进程可能会 ping 这个进程
server = spawn(&server/0)
source_alias = Process.alias()
send(server, {:ping, source_alias})
receive do
:pong -> :pong
end
#=> :pong
如果您现在停用 source_alias
并再次 ping 服务器,您将不会收到任何响应,因为服务器会将 :pong
响应 send/2
到一个停用的别名。
Process.unalias(source_alias)
send(server, {:ping, source_alias})
receive do
:pong -> :pong
after
1000 -> :timeout
end
#=> :timeout
另请参阅Erlang 参考手册中的进程别名部分。
概要
函数
创建进程别名。
创建进程别名。
判断给定进程是否在本地节点上存活。
取消由 send_after/3
返回的计时器。
从进程字典中删除给定的 key
。
取消对由给定 reference
标识的监控器的监控。
向 pid
发送带有给定 reason
的退出信号。
为调用进程设置给定的 flag
为 value
。
为给定的进程 pid
设置给定的 flag
为 value
。
返回进程字典中的所有键值对。
返回进程字典中给定 key
的值,如果 key
未设置,则返回 default
。
返回进程字典中的所有键。
返回进程字典中具有给定 value
的所有键。
返回调用进程的组领导者的 PID。
将给定 pid
的组领导者设置为 leader
。
将调用进程置于“休眠”状态。
返回由 pid
标识的进程的信息,如果进程不存在,则返回 nil
。
返回由 pid
标识的进程的信息,如果进程不存在,则返回 nil
。
在调用进程和给定项(进程或端口)之间创建链接。
返回一个 PID 列表,这些 PID 对应于当前在本地节点上存在的所有进程。
从调用进程开始监控给定的 item
。
从调用进程开始监控给定的 item
。
在进程字典中存储给定的 key
-value
对。
读取由 send_after/3
创建的计时器。
在本地节点上将给定的 pid_or_port
注册到给定的 name
下。
返回使用 register/2
注册的名称列表。
向给定的 dest
发送消息。
在 time
毫秒后将 msg
发送到 dest
。
使当前进程休眠给定的 timeout
时间。
根据给定的选项生成给定的函数。
根据给定的选项,从模块 mod
生成给定的函数 fun
,并将给定的 args
传递给它。
显式停用进程别名。
删除调用进程和给定项(进程或端口)之间的链接。
删除与 PID 或端口标识符关联的已注册的 name
。
返回在 name
下注册的 PID 或端口标识符,如果名称未注册,则返回 nil
。
类型
@type alias() :: reference()
有关别名的更多信息,请参阅模块文档。
@type alias_opt() :: :explicit_unalias | :reply
进程目标。
远程或本地 PID、本地端口、本地注册的名称,或 {registered_name, node}
形式的元组,用于另一个节点上的注册的名称。
@type spawn_opt() :: :link | :monitor | {:monitor, :erlang.monitor_option()} | {:priority, :low | :normal | :high} | {:fullsweep_after, non_neg_integer()} | {:min_heap_size, non_neg_integer()} | {:min_bin_vheap_size, non_neg_integer()} | {:max_heap_size, heap_size()} | {:message_queue_data, :off_heap | :on_heap}
@type spawn_opts() :: [spawn_opt()]
函数
@spec alias() :: alias()
创建进程别名。
这与调用 alias/1
作为 alias([:explicit_unalias])
相同。另请参阅 :erlang.alias/0
。
由编译器内联。
示例
alias = Process.alias()
创建进程别名。
有关别名的更多信息,请参阅模块文档。另请参阅 :erlang.alias/1
。
由编译器内联。
示例
alias = Process.alias([:reply])
判断给定进程是否在本地节点上存活。
如果由 pid
标识的进程存活(即,它没有退出并且尚未退出),则此函数返回 true
。否则,它返回 false
。
pid
必须引用在本地节点上运行的进程,否则会引发 ArgumentError
。
由编译器内联。
@spec cancel_timer(reference(), options) :: non_neg_integer() | false | :ok when options: [async: boolean(), info: boolean()]
取消由 send_after/3
返回的计时器。
当结果是整数时,它表示计时器到期前剩余的时间(以毫秒为单位)。
当结果是 false
时,找不到与 timer_ref
对应的计时器。这可能是由于计时器已过期、已取消或 timer_ref
从未对应于计时器而导致的。
即使计时器已过期且已发送消息,此函数也不会告诉您超时消息是否已到达其目标。
由编译器内联。
选项
:async
- (boolean) 当为false
时,取消请求是同步的。当为true
时,取消请求是异步的,这意味着发出取消计时器的请求,并立即返回:ok
。默认为false
。:info
- (boolean) 是否返回有关被取消计时器的信息。当:async
选项为false
且:info
为true
时,则返回整数或false
(如上所述)。如果:async
为false
且:info
为false
,则返回:ok
。如果:async
为true
且:info
为true
,则当执行取消操作时,将以{:cancel_timer, timer_ref, result}
(其中result
是整数或false
,如上所述)的形式将消息发送到此函数的调用者。如果:async
为true
且:info
为false
,则不会发送任何消息。默认为true
。
从进程字典中删除给定的 key
。
返回进程字典中 key
下的值,如果 key
未存储在进程字典中,则返回 nil
。
示例
iex> Process.put(:comments, ["comment", "other comment"])
iex> Process.delete(:comments)
["comment", "other comment"]
iex> Process.delete(:comments)
nil
取消对由给定 reference
标识的监控器的监控。
如果 monitor_ref
是调用进程通过调用 monitor/1
获得的引用,则该监控将被关闭。如果监控已关闭,则不会发生任何事情。
有关更多信息,请参阅 :erlang.demonitor/2
。
由编译器内联。
示例
pid = spawn(fn -> 1 + 2 end)
ref = Process.monitor(pid)
Process.demonitor(ref)
#=> true
向 pid
发送带有给定 reason
的退出信号。
如果 reason
不是 :normal
或 :kill
,则将应用以下行为。
如果
pid
未捕获退出信号,pid
将以指定的reason
退出。如果
pid
捕获了退出信号,则退出信号将被转换为消息{:EXIT, from, reason}
并传递到pid
的消息队列中。
如果 reason
是原子 :normal
,则 pid
不会退出(除非 pid
是调用进程,在这种情况下它将以 :normal
原因退出)。如果它捕获了退出信号,则退出信号将被转换为消息 {:EXIT, from, :normal}
并传递到其消息队列中。
如果 reason
是原子 :kill
,即如果调用了 Process.exit(pid, :kill)
,则会向 pid
发送一个不可捕获的退出信号,该信号将无条件地以 :killed
原因退出。
由编译器内联。
示例
Process.exit(pid, :kill)
#=> true
@spec flag(:error_handler, module()) :: module()
@spec flag(:max_heap_size, heap_size()) :: heap_size()
@spec flag(:message_queue_data, :off_heap | :on_heap) :: :off_heap | :on_heap
@spec flag(:min_bin_vheap_size, non_neg_integer()) :: non_neg_integer()
@spec flag(:min_heap_size, non_neg_integer()) :: non_neg_integer()
@spec flag(:priority, priority_level()) :: priority_level()
@spec flag(:save_calls, 0..10000) :: 0..10000
@spec flag(:sensitive, boolean()) :: boolean()
@spec flag(:trap_exit, boolean()) :: boolean()
为调用进程设置给定的 flag
为 value
。
返回 flag
的旧值。
有关更多信息,请参阅 :erlang.process_flag/2
。
由编译器内联。
@spec flag(pid(), :save_calls, 0..10000) :: 0..10000
为给定的进程 pid
设置给定的 flag
为 value
。
返回 flag
的旧值。
如果 pid
不是本地进程,则会引发 ArgumentError
。
允许用于 flag
的值只是 flag/2
中允许值的一个子集,即 :save_calls
。
有关更多信息,请参阅 :erlang.process_flag/3
。
由编译器内联。
返回进程字典中的所有键值对。
由编译器内联。
返回进程字典中给定 key
的值,如果 key
未设置,则返回 default
。
示例
# Assuming :locale was not set
iex> Process.get(:locale, "pt")
"pt"
iex> Process.put(:locale, "fr")
nil
iex> Process.get(:locale, "pt")
"fr"
@spec get_keys() :: [term()]
返回进程字典中的所有键。
由编译器内联。
示例
# Assuming :locale was not set
iex> :locale in Process.get_keys()
false
iex> Process.put(:locale, "pt")
nil
iex> :locale in Process.get_keys()
true
返回进程字典中具有给定 value
的所有键。
由编译器内联。
@spec group_leader() :: pid()
返回调用进程的组领导者的 PID。
由编译器内联。
示例
Process.group_leader()
#=> #PID<0.53.0>
将给定 pid
的组领导者设置为 leader
。
通常,这用于当从某个 shell 启动的进程应该拥有一个不同于 :init
的组领导者时。
由编译器内联。
将调用进程置于“休眠”状态。
调用进程将被置于等待状态,在此状态下其内存分配将尽可能减少,如果进程在不久的将来预计不会收到任何消息,这将很有用。
有关更多信息,请参阅 :erlang.hibernate/3
。
由编译器内联。
返回由 pid
标识的进程的信息,如果进程不存在,则返回 nil
。
仅将此用于调试信息。
有关更多信息,请参阅 :erlang.process_info/1
。
返回由 pid
标识的进程的信息,如果进程不存在,则返回 nil
。
有关更多信息,请参阅 :erlang.process_info/2
。
在调用进程和给定项(进程或端口)之间创建链接。
链接是双向的。可以使用 unlink/1
来取消链接进程。
如果此类链接已经存在,则此函数不会执行任何操作,因为两个给定进程之间只能存在一个链接。如果进程试图给自己创建一个链接,则不会发生任何事情。
当两个进程链接时,每个进程都会收到来自对方的退出信号(另请参阅 exit/2
)。假设 pid1
和 pid2
链接。如果 pid2
以除 :normal
之外的某个原因退出(这也是进程完成其工作时使用的退出原因),并且 pid1
未捕获退出信号(请参阅 flag/2
),则 pid1
将以与 pid2
相同的原因退出,并依次向其所有其他链接进程发出退出信号。当 pid1
捕获退出信号时,其行为在 exit/2
中进行了描述。
有关更多信息,请参阅 :erlang.link/1
。
由编译器内联。
@spec list() :: [pid()]
返回一个 PID 列表,这些 PID 对应于当前在本地节点上存在的所有进程。
请注意,如果进程正在退出,则它被认为是存在的,但不是存活的。这意味着对于此类进程,alive?/1
将返回 false
,但其 PID 将是此函数返回的 PID 列表的一部分。
有关更多信息,请参阅 :erlang.processes/0
。
由编译器内联。
示例
Process.list()
#=> [#PID<0.0.0>, #PID<0.1.0>, #PID<0.2.0>, #PID<0.3.0>, ...]
从调用进程开始监控给定的 item
。
一旦被监控的进程死亡,一条消息将以以下形式传递到监控进程:
{:DOWN, ref, :process, object, reason}
其中
ref
是此函数返回的监控引用;object
是被监控进程的pid
(如果监控 PID)或{name, node}
(如果监控远程或本地名称);reason
是退出原因。
如果在调用 Process.monitor/1
时进程已经死亡,则会立即传递一条 :DOWN
消息。
有关示例,请参阅 "监控的必要性"。有关更多信息,请参阅 :erlang.monitor/2
。
由编译器内联。
示例
pid = spawn(fn -> 1 + 2 end)
#=> #PID<0.118.0>
Process.monitor(pid)
#=> #Reference<0.906660723.3006791681.40191>
Process.exit(pid, :kill)
#=> true
receive do
msg -> msg
end
#=> {:DOWN, #Reference<0.906660723.3006791681.40191>, :process, #PID<0.118.0>, :noproc}
@spec monitor(pid() | {name, node()} | name, [:erlang.monitor_option()]) :: reference() when name: atom()
从调用进程开始监控给定的 item
。
此函数类似于 monitor/1
,但接受选项来自定义如何监控 item
。有关这些选项的文档,请参阅 :erlang.monitor/3
。
由编译器内联。
示例
pid =
spawn(fn ->
receive do
{:ping, source_alias} -> send(source_alias, :pong)
end
end)
#=> #PID<0.118.0>
ref_and_alias = Process.monitor(pid, alias: :reply_demonitor)
#=> #Reference<0.906660723.3006791681.40191>
send(pid, {:ping, ref_and_alias})
receive do: msg -> msg
#=> :pong
receive do: msg -> msg
#=> {:DOWN, #Reference<0.906660723.3006791681.40191>, :process, #PID<0.118.0>, :noproc}
在进程字典中存储给定的 key
-value
对。
此函数的返回值是在 key
下面之前存储的值,或者在该值下没有存储任何值的情况下为 nil
。
示例
# Assuming :locale was not set
iex> Process.put(:locale, "en")
nil
iex> Process.put(:locale, "fr")
"en"
@spec read_timer(reference()) :: non_neg_integer() | false
读取由 send_after/3
创建的计时器。
当结果是整数时,它表示计时器到期前剩余的毫秒数。
当结果是 false
时,无法找到与 timer_ref
相对应的计时器。这可能是因为计时器已经过期,因为它已经被取消,或者因为 timer_ref
从未对应于计时器。
即使计时器已过期且已发送消息,此函数也不会告诉您超时消息是否已到达其目标。
由编译器内联。
在本地节点上将给定的 pid_or_port
注册到给定的 name
下。
name
必须是一个原子,然后可以用它代替 PID/端口标识符,使用 Kernel.send/2
发送消息。
register/2
将在以下任何情况下失败,并引发 ArgumentError
- PID/端口在本地不存在且存活
- 该名称已经注册
pid_or_port
已经在另一个name
下注册
以下名称是保留的,不能分配给进程或端口
nil
false
true
:undefined
示例
Process.register(self(), :test)
#=> true
send(:test, :hello)
#=> :hello
send(:wrong_name, :hello)
** (ArgumentError) argument error
@spec registered() :: [atom()]
返回使用 register/2
注册的名称列表。
由编译器内联。
示例
Process.register(self(), :test)
Process.registered()
#=> [:test, :elixir_config, :inet_db, ...]
@spec send(dest, msg, [option]) :: :ok | :noconnect | :nosuspend when dest: dest(), msg: any(), option: :noconnect | :nosuspend
向给定的 dest
发送消息。
dest
可以是远程或本地 PID、本地端口、本地注册的名称,或者是一个元组,形式为 {registered_name, node}
,用于另一个节点上的注册的名称。
由编译器内联。
选项
:noconnect
- 当使用时,如果发送消息需要自动连接到另一个节点,则不会发送消息,并且将返回:noconnect
。:nosuspend
- 当使用时,如果发送消息会导致发送方被挂起,则不会发送消息,并且将返回:nosuspend
。
否则,消息将被发送,并且将返回 :ok
。
示例
iex> Process.send({:name, :node_that_does_not_exist}, :hi, [:noconnect])
:noconnect
@spec send_after(pid() | atom(), term(), non_neg_integer(), [option]) :: reference() when option: {:abs, boolean()}
在 time
毫秒后将 msg
发送到 dest
。
如果 dest
是一个 PID,它必须是本地进程的 PID,无论存活与否。如果 dest
是一个原子,它必须是注册进程的名称,该名称将在传递时进行查找。如果名称不引用进程,则不会产生错误。
消息不会立即发送。因此,即使 time
为 0
,dest
也可以在其间接收其他消息。
此函数返回一个计时器引用,可以使用 read_timer/1
读取或使用 cancel_timer/1
取消。
如果给定的 dest
是一个不存在的 PID 或给定的 PID 退出,则计时器将自动取消。请注意,当 dest
是一个原子时,计时器不会自动取消(因为原子解析是在传递时完成的)。
由编译器内联。
选项
:abs
- (布尔值) 当为false
时,time
被视为相对于当前单调时间。当为true
时,time
是msg
应该传递到dest
的 Erlang 单调时间的绝对值。要了解更多有关 Erlang 单调时间和其他时间相关概念的信息,请查看System
模块的文档。默认为false
。
示例
timer_ref = Process.send_after(pid, :hi, 1000)
@spec sleep(timeout()) :: :ok
使当前进程休眠给定的 timeout
时间。
timeout
既可以是作为整数的睡眠毫秒数,也可以是原子 :infinity
。当给出 :infinity
时,当前进程将永远睡眠,并且不会使用或回复消息。
请谨慎使用此函数。对于几乎所有在 Elixir 中使用 sleep/1
的情况,都可能存在更正确、更快、更精确的方法来使用消息传递来实现相同的效果。
例如,如果您正在等待进程执行某个操作,最好使用消息传递来传达该操作的进度。
换句话说,不要
Task.start_link(fn ->
do_something()
...
end)
# Wait until work is done
Process.sleep(2000)
但要
parent = self()
Task.start_link(fn ->
do_something()
send(parent, :work_is_done)
...
end)
receive do
:work_is_done -> :ok
after
# Optional timeout
30_000 -> :timeout
end
对于上述情况,Task.async/1
和 Task.await/2
是首选。
类似地,如果您正在等待进程终止,请监控该进程而不是睡眠。不要
Task.start_link(fn ->
...
end)
# Wait until task terminates
Process.sleep(2000)
而是要
{:ok, pid} =
Task.start_link(fn ->
...
end)
ref = Process.monitor(pid)
receive do
{:DOWN, ^ref, _, _, _} -> :task_is_down
after
# Optional timeout
30_000 -> :timeout
end
@spec spawn((-> any()), spawn_opts()) :: pid() | {pid(), reference()}
根据给定的选项生成给定的函数。
结果取决于给定的选项。特别地,如果 :monitor
被作为选项给出,它将返回一个包含 PID 和监控引用的元组,否则只返回生成的进程 PID。
还有更多选项可用;有关可用选项的完整列表,请查看 :erlang.spawn_opt/4
。
由编译器内联。
示例
Process.spawn(fn -> 1 + 2 end, [:monitor])
#=> {#PID<0.93.0>, #Reference<0.18808174.1939079169.202418>}
Process.spawn(fn -> 1 + 2 end, [:link])
#=> #PID<0.95.0>
根据给定的选项,从模块 mod
生成给定的函数 fun
,并将给定的 args
传递给它。
结果取决于给定的选项。特别地,如果 :monitor
被作为选项给出,它将返回一个包含 PID 和监控引用的元组,否则只返回生成的进程 PID。
它还接受额外的选项,有关可用选项的列表,请查看 :erlang.spawn_opt/4
。
由编译器内联。
显式停用进程别名。
如果 alias
是当前进程的当前活动别名,则返回 true
,否则返回 false
。
有关别名的更多信息,请参见 模块文档。另请参见 :erlang.unalias/1
。
由编译器内联。
示例
alias = Process.alias()
Process.unalias(alias)
#=> true
删除调用进程和给定项(进程或端口)之间的链接。
如果不存在这样的链接,则此函数不执行任何操作。如果 pid_or_port
不存在,则此函数不会产生任何错误,并且只是不执行任何操作。
此函数的返回值始终为 true
。
有关更多信息,请参见 :erlang.unlink/1
。
由编译器内联。
@spec unregister(atom()) :: true
删除与 PID 或端口标识符关联的已注册的 name
。
如果该名称未注册到任何 PID 或端口,则会引发 ArgumentError
错误。
由编译器内联。
示例
Process.register(self(), :test)
#=> true
Process.unregister(:test)
#=> true
Process.unregister(:wrong_name)
** (ArgumentError) argument error
返回在 name
下注册的 PID 或端口标识符,如果名称未注册,则返回 nil
。
有关更多信息,请参见 :erlang.whereis/1
。
示例
Process.register(self(), :test)
Process.whereis(:test)
#=> #PID<0.84.0>
Process.whereis(:wrong_name)
#=> nil