如何在Erlang中动态创建原子?

时间:2014-03-09 00:15:29

标签: erlang

我正在尝试使用动态创建的原子名称注册几个进程,如下所示:

keep_alive(Name, Fun) ->
    register(Name, Pid = spawn(Fun)),
    on_exit(Pid, fun(_Why) -> keep_alive(Name, Fun) end).

monitor_some_processes(N) ->
    %% create N processes that restart automatically when killed
    for(1, N, fun(I) ->
                             Mesg = io_lib:format("I'm process ~p~n", [I]),
                             Name = list_to_atom(io_lib:format("zombie~p", [I])),
                             keep_alive(Name, fun() -> zombie(Mesg) end)
                     end).

for(N, N, Fun) -> [Fun(N)];
for(I, N, Fun) -> [Fun(I)|for(I+1, N, Fun)].

zombie(Mesg) ->
    io:format(Mesg),
    timer:sleep(3000),
    zombie(Mesg).

list_to_atom/1调用导致错误:

43> list_to_atom(io_lib:format("zombie~p", [1])).
** exception error: bad argument
     in function  list_to_atom/1
        called as list_to_atom([122,111,109,98,105,101,"1"])

我做错了什么? 还有,有更好的方法吗?

2 个答案:

答案 0 :(得分:5)

<强> TL; DR

你不应该动态生成原子。从您的代码片段中可以看出,您可能正在尝试找到一些方法来灵活地命名进程,但原子不是它。使用某种类型的K / V商店而不是register/2

<强>讨论

原子是限制性的原因。它们应该代表程序的永恒结构,而不是当前的状态。原子是如此限制,以至于我想象你真正希望能够做的是使用任意Erlang值注册一个进程,而不仅仅是原子,并更自由地引用它们。

如果是这种情况,请选择以下四种方法之一:

  1. 将键/值对保留在某处以充当您自己的注册表。这可以是一个单独的进程或list / tree / dict / map处理程序来存储#{Name => Pid}的键/值对。
  2. 使用global模块(与下面的gproc一样,具有适用于群集的功能)。
  3. 使用像Ulf Wiger这样漂亮的小项目gproc之类的注册表解决方案。当你真正需要它的时候真是太棒了(老实说,不像我看到的那样频繁)。这是一篇关于它的使用及其工作方式的博客文章:http://blog.rusty.io/2009/09/16/g-proc-erlang-global-process-registry/。 gproc的一个额外优势是,您遇到的几乎每个Erlanger至少都是熟悉它。
  4. 第一个选项的变体,将您的程序构建为服务管理员和工作者树(如"Service -> Worker Pattern"中所述)。这种模式的一个副作用是,经常服务管理员如果你做了一件非常重要的事情,就会出于某种原因需要监控其流程,这使得它成为理想的候选者。保持Pids的Key / Value注册表的地方。当程序成熟时,这种模式很自然地出现,如果该程序具有高稳健性要求,则尤其。将其构建为一组半独立服务,从一开始就在每个服务的顶部构建一个抽象的管理界面,这通常是一种方便的进化捷径。

答案 1 :(得分:2)

io_lib:format返回潜在的“深层列表”(即它可能包含其他列表),而list_to_atom则需要“平面列表”。您可以在io_lib:format

的电话中打包lists:flatten来电
list_to_atom(lists:flatten(io_lib:format("zombie~p", [1]))).