【问题标题】:How to save state in an Erlang process?如何在 Erlang 进程中保存状态?
【发布时间】:2012-05-24 22:42:13
【问题描述】:

我正在学习 Erlang,并试图弄清楚我可以并且应该如何在进程中保存状态。

例如,我正在尝试编写一个程序,它给出文件中的数字列表,告诉我该文件中是否出现数字。我的方法是使用两个进程

  • 缓存,它将文件的内容读入一个集合,然后等待数字检查,然后回复它们是否出现在集合中。

    is_member_loop(Data_file) ->
        Numbers = read_numbers(Data_file),
        receive
            {From, Number} ->
                From ! {self(), lists:member(Number, Numbers)},
                is_member_loop(Data_file)
        end.
    
  • 客户端将数字发送到缓存并等待truefalse响应。

    check_number(Number) ->
        NumbersPid ! {self(), Number},
        receive
            {NumbersPid, Is_member} ->
                Is_member
        end.
    

这种方法显然是幼稚的,因为每次请求都会读取文件。但是,我是 Erlang 的新手,我不清楚在不同请求之间保持状态的首选方式是什么。

我应该使用进程字典吗?对于这种进程状态,是否存在我不知道的不同机制?

更新

user601836 建议的最明显的解决方案是将一组数字作为参数传递给is_member_loop,而不是文件名。这似乎是 Erlang 中的一个常见习语,在奇妙的在线书籍Learn you some Erlang 中有一个很好的例子。

但我认为,对于我想在我的流程中保留的更复杂的状态,这个问题仍然存在。

【问题讨论】:

    标签: erlang


    【解决方案1】:

    简单的解决方案,您可以将数字列表而不是文件名传递给您的函数is_member_loop(Data_file)

    处理状态的最佳解决方案是使用 gen_server。要了解更多信息,您应该查看 recordsgen_server behaviourthis 也可能有用)。

    在实践中:

    1) 从基于 gen_server 行为的模块 (yourmodule.erl) 开始 2)在 gen_server 的 init 函数中读取您的文件并将其作为状态字段传递:

    init([]) ->
        Numbers = read_numbers(Data_file),
    {ok, #state{numbers=Numbers}}.
    

    3) 编写一个函数,用于触发对 gen_server 的调用

    check_number(Number) ->
        gen_server:call(?MODULE, {check_number, Number}).
    

    4) 编写代码以处理函数触发的消息

    handle_call({check_number, Number}, _From, #state{numbers=Numbers} = State) ->
        Reply = lists:member(Number, Numbers)},
    {reply, Reply, State};
    
    handle_call(_Request, _From, State) ->
        Reply = ok,
    {reply, Reply, State}.
    

    5) 从 yourmodule.erl 函数导出 check_number

    -export([check_number/1]).
    

    关于第 4 点需要说明两点:

    a) 我们使用模式匹配提取记录状态中的值

    b) 如您所见,我离开了通用句柄调用,否则每当收到与 {check_number, Number} 不同的消息时,您的 gen_server 将由于错误的模式匹配而失败

    注意:如果您是 erlang 新手,请不要使用进程字典

    【讨论】:

    • 我几乎觉得自己很愚蠢,没想到自己将列表本身作为参数而不是文件名传递。我正在考虑流程字典,因为在Programming Erlang 中,Joe Armstrong 在他的一个示例中使用了它。我在网上读到这是一种不鼓励的做法,因此我提出了问题。最后,我是一个新手,还没有达到 OTP 部分,但gen_server 似乎确实是更好的方法。
    【解决方案2】:

    不确定这是多么地道,因为我还不是 Erlang 专业人士,但我会使用 ETS 来处理这个问题。基本上,

    read_numbers_to_ets(DataFile) ->
        Table = ets:new(numbers, [ordered_set]),
        insert_numbers(Table, DataFile),
        Table.
    
    insert_numbers(Table, DataFile) ->
        case read_next_number(DataFile) of
        eof -> ok;
        Num -> ets:insert(numbers, {Num})
        end.
    

    然后您可以将您的is_member 定义为

    is_member(TableId, Number) ->
        case ets:match(TableId, {Number}) of
        [] -> false; %% no match from ets
        [[]] -> true %% ets found the number you're looking for in that table
        end.
    

    您的is_member_loop 不会使用Data_file,而是使用表的ID 进行查找。

    【讨论】:

    • ets 是一种解决方案,但您必须小心,因为如果所有者进程崩溃,您的 ets 表也会丢失。
    猜你喜欢
    • 2023-03-27
    • 2012-10-23
    • 2010-11-19
    • 1970-01-01
    • 2013-11-30
    • 1970-01-01
    • 2015-10-23
    • 1970-01-01
    • 2017-11-12
    相关资源
    最近更新 更多