【问题标题】:Erlang Dictionary fetch crashErlang Dictionary 获取崩溃
【发布时间】:2011-05-19 16:50:42
【问题描述】:

编辑


我有两个模块,在从字典中获取时都会导致错误的 args 错误(gen_server 状态)

这是来自一个模块的代码

init([ChunkSize, RunningCounter]) ->    
D0 = dict:new(),
D1 = dict:store(chunkSize, ChunkSize, D0),
D2 = dict:store(torrentUploadSpeed, 0, D1),
D3 = dict:store(torrentDownloadSpeed, 0, D2),
TorrentDownloadQueue = queue:new(),
TorrentUploadQueue = queue:new(),
D4 = dict:store(torrentDownloadQueue, TorrentDownloadQueue, D3),
D5 = dict:store(torrentUploadQueue, TorrentUploadQueue, D4),
D6 = dict:store(runningCounter, RunningCounter, D5),
{ok, D6}.

然后我 set_peer_state 设置一个对等体字典(每个对等体 1 个唯一)该字典保存下载和上传(队列和速度),我将其添加到主 gen_server 状态(字典)所以我有主要的 torrent 数据在主字典中,每个对等点都有一个字典,由对等点 ID 存储。

set_peer_state(Id) ->
    gen_server:cast(?SERVER, {setPeerState, Id}).

handle_cast({setPeerState, Id}, State) ->
io:format("In the Set Peer State ~p~n", [dict:fetch(runningCounter, State)]),
Id0 = dict:new(), 
PeerDownloadQueue = queue:new(),
PeerUploadQueue = queue:new(),
Id1 = dict:store(peerDownloadQueue, PeerDownloadQueue, Id0),
Id2 = dict:store(peerUploadQueue, PeerUploadQueue, Id1),
Id3 = dict:store(peerDownloadSpeed, 0, Id2),
Id4 = dict:store(peerUploadSpeed, 0, Id3),
D = dict:store(Id, Id4, State),
    {noreply, D};       

到目前为止,这似乎有效。但是当我尝试更新种子状态时,它会在从字典中获取时崩溃。

handle_cast({updateTorrentDownloadState, Time}, State) ->
% fetch the counter for the speed calculation and queue length
RunningCounter  = dict:fetch(runningCounter, State),
% Fetch the Torrents download queue
TorrentDownloadQueue = dict:fetch(torrentDownloadQueue, State),
io:format("The fetched queue is  ~p~n", [dict:fetch(torrentDownloadQueue, State)]),
% Add the item to the queue (main torrent upload queue)
TorrentDownloadQueue2 = queue:in(Time, TorrentDownloadQueue),
% Get the lenght of the downloadQueue
TorrentDownloadQueueLength = queue:len(TorrentDownloadQueue2),
% If the queue is larger than the running counter remove item 
if
    TorrentDownloadQueueLength >= RunningCounter ->
        % Remove item from the queue
        TorrentDownloadQueue3 = queue:drop(TorrentDownloadQueue2),
        update_torrent_download(TorrentDownloadQueue3, State);

    TorrentDownloadQueueLength < RunningCounter ->
        update_torrent_download(TorrentDownloadQueue2, State)
    end;            

这是两个内部函数

update_torrent_download(TorrentDownloadQueue, State) ->
    % Store the queue to the new torrent dict
    State2  = dict:store(torrentDownLoadQueue, TorrentDownloadQueue, State),
    Speed = calculate_speed(TorrentDownloadQueue, State2),
    State3 = dict:store(torrentDownloadSpeed, Speed, State2),
        {noreply, State3}.

calculate_speed(Queue, State) ->
List = queue:to_list(Queue),
Sum = lists:sum(List),
Count = queue:len(Queue),
ChunkSize = dict:fetch(chunkSize, State),
Speed = (Count * ChunkSize) div Sum,
    {ok, Speed}.

会不会是向 setter 传递不正确的数据导致服务器崩溃? 还是国家在此过程中迷失了方向? 这种做法似乎让所有新字典都存储在旧字典中很乱,有没有更好的方法来处理这种数据结构(每个节点的主要种子和数据)?

我知道我可以从列表中制作字典,但是在我测试这个模块的时候它让我的想法变得混乱。

谢谢

【问题讨论】:

    标签: erlang


    【解决方案1】:

    您的问题是 State 不是字典。

    1> dict:fetch(runningCounter, not_a_dict).
    ** exception error: {badrecord,dict}
         in function  dict:get_slot/2
         in call from dict:fetch/2
    

    【讨论】:

    • 谢谢。所以我在整个模块中使用 State。我以为这是由 gen serve 作为国家处理的? State 应该在 gen server 中定义为字典吗?
    • gen_server:cast 方法只传递一个消息,而handle_cast 接受消息和State。它怎么知道状态是什么?我在哪里设置这个状态?我在设置它的 init 方法中执行此操作,但看起来它在 handle_cast 方法中不可用?谢谢
    • 问题是您在程序的某个地方做错了事,但错误不在您共享的代码中。 State 只是一个变量,但它不是您尝试使用它的字典,这意味着您在其他地方错误地设置了它。
    • 如果只从字典中获取,如何设置字典?我从句柄调用中的字典中获取并回复计算结果。然后我在 handle_call 结束时将 State 传回。所以状态不是保存为字典。如果我只从中获取,我应该如何创建一个字典来传递给 { , Response, State} ?谢谢
    • 对不起,我不太明白你的问题,请修正代码格式并包含所有内容。
    【解决方案2】:

    正如您的参数有效建议的那样,您的状态,在您的代码中,不是一个字典。

    现在回答你的 cmets。

    gen_server 的状态在 init 函数中设置,返回:{ok, State}.

    每次您的gen_server 收到消息时,都会调用handle_callhandle_cast(取决于调用是同步的还是异步的)。在这些函数中,您在初始化阶段设置的状态可以更新并转换为任何内容。您不能假设在服务器的整个执行过程中初始状态的“类型”是相同的。

    换句话说,如果您执行以下操作:

    init(_Args) -> {ok, dict:new()}.
    
    handle_call(_Request, _From, State) ->
      {ok, completely_new_state}.
    

    你刚刚将你的状态从一个字典“转换”成一个原子,而那个(原子)就是你在后续调用中会得到的。

    对于此类错误,Erlang 跟踪工具dbg 非常有帮助,可以让您查看函数是如何调用的以及返回了哪些结果。看看这个简短的教程来了解如何使用它:

    http://aloiroberto.wordpress.com/2009/02/23/tracing-erlang-functions/

    更新:

    你应该做什么:

    init(_Args) -> {ok, dict:new()}.
    
    handle_call(_Request, _From, State) ->
      NewState = edit_dict(State),
      {ok, NewState}.
    

    edit_dict 函数是一个接受一个字典并返回一个更新的字典的函数。

    【讨论】:

    • 太棒了。那么我应该在handle_call、handle_cast 函数的开头创建一个新的dict = State 吗?如果类型发生变化,键值对是否仍然相同或者状态的意义何在?
    • 谢谢。编辑字典是什么或如何工作的?我不明白。这就是 gen_servers 与他们的状态一起工作的方式吗?有点烂那么创建一个新的字典并与它合并状态呢?或者状态不被视为字典?谢谢
    • 示例的edit_dict函数是一个你应该编写的辅助函数。 gen_servers 管理其状态的方式没有什么糟糕的。一旦你得到它背后的想法,你就会爱上它。合并部分是 edit_dict 函数应该做的。看看 dict:update 函数。如果您分享更多代码,我们将更容易发表评论。
    • 我再次对此进行了测试,从字典值中删除了 [ ]。它似乎仍然没有传递状态。我可以从字典中获取并返回,但是当我尝试从字典中获取并计算它时会发生崩溃。我认为这是我调用这些的顺序。在第一次调用时一切正常,它会获取,但在下一次调用时它会崩溃。我显然在句柄调用后错误地传递了状态。如果您不介意看一下,我用代码更新了我的问题。非常感谢。
    猜你喜欢
    • 2011-02-16
    • 2013-11-23
    • 1970-01-01
    • 2016-05-20
    • 2019-03-08
    • 1970-01-01
    • 2015-03-04
    • 2011-08-17
    • 2021-08-09
    相关资源
    最近更新 更多