2

我目前正在以合理的方式学习 Erlang,但对主管的 gen_server 有疑问。如果 gen_server 进程崩溃并因此被主管重新启动,它会收到一个新的 pid。现在,如果我希望其他进程通过 Pid 引用该进程怎么办?在这些过程中“更新” Pid 有哪些好的惯用方法?

作为一些实际应用的练习,我正在编写一个锁服务器,客户端可以使用任意键请求锁。理想情况下,我希望有一个单独的进程来处理特定锁的锁定和释放,我的想法是如果在 N 时间后没有人请求它,我可以使用 gen_server 中的 timeout 参数来终止进程,因此只有当前相关的锁将留在内存中。现在,我有一个将锁名称映射到锁进程的目录进程。当锁定进程终止时,它会从目录中删除锁定。

我关心的是如何处理客户端请求锁定而锁定过程正在终止的情况。它还没有关闭,所以嗅探 pid 是活着的是行不通的。锁定过程尚未到达将其从目录中删除的子句。

有没有更好的方法来处理这个?

编辑

当前有两个 gen_server:“目录”维护来自 LockName -> Lock Process 的 ETS 表,以及使用 start_child 动态添加到监督树的“锁定服务器”。理想情况下,我希望每个锁服务器直接处理与客户端的对话,但我担心当进程处于崩溃中间时,获取/释放请求会被调用或强制转换发出(因此不会响应)到消息)。

以 {local} 或 {global} 开头将不起作用,因为它们可能有 N 个。

4

3 回答 3

5

诀窍是给进程命名,而不是通过它的 pid 来引用它。您通常有 3 个可行的选择,

  • 使用注册名称。这就是 andreypopp 的建议。您通过其注册名称引用服务器。本地注册的名称必须是原子,这可能会限制你。全球注册的名字没有这个限制,你可以注册任何一个词。

  • 主管知道 Pid。问它。您必须将 Supervisor Pid 传递给流程。

  • 或者,使用 gproc 应用程序(位于http://github.com上)。它允许你创建一个通用的进程注册表——你可以通过 ETS 来完成,但是窃取好的代码而不是自己实现。

如果所有进程都是同一监督树的一部分,则 pid 可用。因此,其中一个人的死亡意味着其他人的死亡。因此,Pids 回收无关紧要。

于 2010-11-14T22:24:26.313 回答
4

不要通过 pid 引用 gen_server 进程。

gen_server:call/2您应该通过or函数为您的 gen_server 提供 API gen_server:call/3。它们被接受ServerRef为第一个参数,可以是Name | {Name,Node} | {global,GlobalName} | pid(). 所以,你的 API 看起来像:

lock(Key) ->
  gen_server:call(?MODULE, {lock, Key}).
release(Key) ->
  gen_server:call(?MODULE, {release, Key}).

请注意,此 API 与您的 gen_server 在同一模块中定义,我假设您使用以下内容启动服务器:

gen_server:start_link({local, ?MODULE}, ?MODULE, [], [])

因此,您的 API 方法可以不是通过 pid 而是通过服务器名称来查找服务器,该名称等于?MODULE.

有关详细信息,请参阅gen_server 文档

于 2010-11-14T20:55:39.617 回答
1

通过使用“erlang:monitor/demonitor”API,您可以完全避免使用“lock_server”进程。

当客户端请求锁时,您发出锁.. 并在客户端上执行 erlang:monitor.. 这将返回一个 Monitor Reference.. 然后您可以将此 Reference 与您的锁一起存储.. 这样做的美妙之处在于当客户端死亡时将通知您的目录服务器..您可以在客户端中实现 TIMEOUT 事情。

这是我最近编写的代码片段.. https://github.com/xslogic/phoebus/blob/master/src/table_manager.erl

基本上,table_manager 是一个向客户端发出锁定特定表资源的进程。如果客户端死了,表将返回到池中。

于 2010-11-15T08:14:03.807 回答