【问题标题】:ETS does not seem to store my insertETS 似乎没有存储我的插入
【发布时间】:2019-02-02 20:27:37
【问题描述】:

我正在尝试实现一个可以查询/更新某些状态信息的过程(我正在使用 SMS 服务并希望根据响应存储一些本地数据 - 稍后我将使用数据库,但现在我想使用 ETS,这是我的第一个 Erlang 项目,所以我认为它对学习很有用)。不幸的是,我的插入似乎没有通过,我不明白为什么。这是模块:

-module(st).
-compile(export_all).

maintain_state() ->
    Tab = ets:new(state, [set]),
    receive
        {Pid, lookup, Key} ->
            Pid ! ets:lookup(Tab, Key),
            maintain_state();
        {Pid, update, Key, Handler} ->
            NewState = Handler(ets:lookup(Tab, Key)),
            Status = ets:insert(Tab, NewState),
            Pid ! {Status, NewState},
            maintain_state();
        {Pid, statelist} ->
            Pid ! ets:tab2list(Tab),
            maintain_state();
        kill ->
            void
    end,
    ets:delete(Tab).

start_state_maintainer() ->
    Pid = spawn(st, maintain_state, []),
    register(state, Pid).

update_state(StateHandler) ->
    state ! {self(), update, testing, StateHandler},
    receive
        After ->
            After
    after 1000 ->
            throw("Timeout in update_state")
    end.

lookup_state() ->
    state ! {self(), lookup, testing},
    receive
        Value ->
            Value
    after 1000 ->
            throw("Timeout in lookup_state")
    end.

all_state() ->
    state ! {self(), statelist},
    receive
        Value ->
            Value
    after 1000 ->
            throw("Timeout in all_state")
    end.

然后我在 erl 会话中加载:

> c(st).
> st:start_state_maintainer().
> st:lookup_state().
[]
> st:update_state(fun (St) -> {testing, myval} end).
{true, {testing, myval}}
> st:all_state().
[]

由于update_state 显示true 我认为插入成功,但表中似乎没有存储任何内容。我做错了什么?

PS:如果整个方法有缺陷,或者您对我的代码有其他评论,我也将不胜感激。

【问题讨论】:

    标签: erlang ets


    【解决方案1】:

    好的。让我们再次运行您的代码。

    1> c(st). % compile your code
    {ok,st}
    
    % Before doing anything. let's get count of all ETS tables using ets:all/0
    2> length(ets:all()). 
    16 % So the Erlang VM has 16 tables after starting it
    
    3> st:start_state_maintainer().
    true
    
    % Let's check count of tables again:
    4> length(ets:all()).          
    17 % Your process has created its own table
    
    5> st:lookup_state().
    []
    
    % Check count of tables again
    6> length(ets:all()).
    18 % Why????
    
    7> st:update_state(fun (St) -> {testing, myval} end).
    {true,{testing,myval}}
    8> length(ets:all()).                                
    19
    
    9> st:all_state().   
    []
    10> length(ets:all()).
    20
    

    所以在函数 maintain_state/0 的第 5 行中,您正在创建一个 ETS 表,而在第 9、14 和 17 行中,您再次调用了这个函数!因此,在收到每条消息后(void 除外),您正在创建新的 ETS 表!
    让我们看看这些表格:

    11> P = whereis(state). % Get process id of 'state' and assign it to P
    <0.66.0>
    12> Foreach = 
    fun(Tab) -> 
        case ets:info(Tab, owner) of 
            P -> % If owner of table is state's pid
                io:format("Table ~p with data ~p~n"
                          ,[Tab, ets:tab2list(Tab)]); 
            _ -> 
                ok
        end 
    end.
    #Fun<erl_eval.6.118419387>
    
    13> lists:foreach(Foreach, ets:all()).
    Table 28691 with data []
    Table 24594 with data []
    Table 20497 with data [{testing,myval}]
    Table 16400 with data []
    ok
    

    杀死你的进程后,我们应该再次拥有 16 个表:

    14> exit(P, kill).
    true
    15> length(ets:all()).                                                                                                                      
    16
    

    你有两个选择。您可以像这样使用命名表:

    maintain_state() ->
        % With 'named_table' option, we can use the name of table in code:
        Tab = ets:new(state, [set, named_table]),
        maintain_state2().
    
    maintain_state2() ->
        receive
            {Pid, lookup, Key} ->
                Pid ! ets:lookup(state, Key), % I used name of table
                maintain_state2();
    ...
    

    或者使用表格作为maintain_state2的参数:

    maintain_state() ->
        Tab = ets:new(state, [set]),
        maintain_state2(Tab).
    
    maintain_state2(Tab) ->
        receive
            {Pid, lookup, Key} ->
                Pid ! ets:lookup(Tab, Key),
                maintain_state2(Tab);
    ...
    

    我将代码更改为上述示例之一,结果如下:

    1> st:start_state_maintainer().
    true
    2> st:lookup_state().
    []
    3> st:update_state(fun (St) -> {testing, myval} end).
    {true,{testing,myval}}
    4> st:all_state().
    [{testing,myval}]
    5> length(ets:all()).
    17
    

    在玩过 Erlang 的消息传递并理解了它的功能和概念之后,我真的建议你学习 OTP 设计原则和 OTP 行为,例如 gen_server 并使用它们,而不是编写自己的 receive ...Pid ! ... 语句。

    【讨论】:

    • 啊,我明白了,我应该能够捕捉到那个错误:) 我想也许有一些我不知道的交易系统,我需要提交我的插入。感谢您提供有关 OTP 的建议,我一定会调查的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-06-04
    • 2011-11-02
    • 2014-09-19
    • 2023-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多