我在这里和其他地方读过几条评论,暗示Erlang的流程字典是一个坏主意,应该死掉。通常,作为一个总的Erlang新手,我只是避免它。但是,在这种情况下,我的其他选择并不是很好。
我有一个主调度函数,看起来像这样:
dispatch(State) ->
receive
{cmd1, Params} ->
NewState = do_cmd1_stuff(Params, State),
dispatch(NewState);
{cmd2, Params} ->
NewState = do_cmd2_stuff(Params, State),
dispatch(NewState);
BadMsg ->
log_error(BadMsg),
dispatch(State)
end.
显然,我的名字对我来说更有意义,但这就是它的要点。在由do_cmd2_stuff()调用的函数调用的函数调用的函数中,我想向所有用户发送消息,告诉他们我做过的事情。为此,我需要从发送消息的位置获取用户列表。用户列表不容易粘在全局状态,因为这只是一个数据结构,代表我操作的唯一数据块。
我看到它的方式,除了使用进程字典之外,我还有一些令人不快的选择。我可以通过所有不同级别的功能将用户列表发送到播放广播的最底层。这是令人不愉快的,因为它导致我的所有功能获得一个参数,无论他们是否真的关心它。
或者,我可以让所有do_cmdN_stuff()
函数返回要发送的消息。但这并不好,因为发送消息可能不是我想要做的最后一件事,它使我的调度程序与一堆{Msg, NewState}
元组混乱。此外,某些功能可能在某些时候没有任何消息发送。
就像我之前说过的,我对Erlang很新。也许拥有更多经验的人可以指出我更好的方式。有吗?在这种情况下,流程字典是否合适?
答案 0 :(得分:4)
一个简单的答案是将全局嵌套在一个状态记录中,然后在系统中进行线程化,至少在停止级别。这样可以轻松地在将来向状态添加新字段,而不是罕见的情况,并允许您保持全局状态数据结构不变。所以最初
-record(state, {users=[],state_data}).
将其定义为记录可以在必要时轻松访问和扩展。
答案 1 :(得分:3)
一般规则是,如果您有疑问,则不应使用流程词典。
如果你提到的两个选项不够好(我个人喜欢你要发送消息的那个)你想要的是一些特定的代码来跟踪用户并将消息转发给他们,也许是你的我想做的是拥有一个持有该信息的流程。
Pid ! {forward, Msg}
其中Pid
将负责将所有内容发送到其他一些进程。现在,您仍然需要传递Pid,除非您在某个注册表中为其命名以找到它。使用register/2
,global
或gproc
。
答案 2 :(得分:1)
正如你所提到的,你总是可以将用户列表作为额外的参数传递,这不是那么糟糕。
如果您不想这样做,只需将其置于状态。只有包含用户列表的计算部分才能有一个特殊的状态。
然后总是有可能把它放在ETS或其他服务器进程中。
要做什么很难推荐,因为它很大程度上取决于您的确切应用和偏好。
只需从上述可能性中进行选择,就好像进程字典不存在一样。如果没有变体看起来很优雅,你的代码可能需要重组,如果没有流程字典,总会有更好的方法。
它真的很糟糕它仍然存在,因为它对很多初级Erlang用户很有吸引力。
答案 3 :(得分:1)
你真的不应该使用进程字典。我只接受使用字典
广告1.不是您的情况,您在服务器中使用。广告2.我不知道你的情况。广告3.不是你的情况,因为你需要收件人列表,所以你不会从该过程字典中获得任何东西是非常快速的键/值存储。在您的情况下,我认为您不应该为State
添加所需内容。恕我直言State
正好适合它。
答案 4 :(得分:1)
这是一个有趣的问题,因为它涉及功能设计的基础。
我的意见: 尝试尽可能使函数返回消息,然后发送它们。这很好地区分了两个不同的任务,并将纯粹的功能任务与导致副作用的任务分开。
如果这是不可能的,即使它有点乱,也将接收器作为参数传递。如果广播功能使用该数据,则应明确给予该数据,以便清晰和可预测。
使用ETS作为Peer Stritzinger建议实际上并不比PD更好,两者都隐藏了广播功能使用接收器列表并使其依赖于全局数据这一事实。
我不确定在一个过程中封装某个状态的Erlang方式,正如我给出了可怕的建议所暗示的那样。 ETS或PD真的更好吗?
将我的调度员弄得一团糟 of {Msg,NewState}
这也是我的经验,你经常会这样结束。它不是特别漂亮,但功能设计似乎鼓励这一点。是否可以引入一些语言功能,使其更加美观和自然?
6年前我写道:
是否可以引入一些语言功能以使其更美观和自然?
在学习了很多关于函数式编程的知识之后,我意识到这个例子是在Haskell中找到的monad和do-notation。
答案 5 :(得分:0)
我会考虑从调用堆栈内部向self()
发送一条特殊消息,并在您已绘制的顶级dispatch
方法中处理该消息,其中用户列表可用