【问题标题】:Can't send anything to spawned Erlang process无法向生成的 Erlang 进程发送任何内容
【发布时间】:2015-01-03 11:45:03
【问题描述】:

我有以下 Erlang 代码:

#!/usr/bin/env escript
%%! -pz ../deps/amqp_client ../deps/rabbit_common ../deps/amqp_client/ebin ../deps/rabbit_common/ebin

% RMQ module
-module(rmq).
-export([main/1, send/1, validate/0, test/0]).
-include_lib("../deps/amqp_client/include/amqp_client.hrl").

main(_) ->
    %send(<<"test_esio">>),
    %validate(),
    Pid = spawn(rmq, test, []),
    % Pid = spawn(fun() -> test() end), <= I've tried this way too
    Pid ! s.

test() ->
    receive
        s ->
            io:format("BAR ~n"),
            send(<<"esio">>),
            test();
        get ->
            validate(),
            test();
        _ ->
            io:format("FOO"),
            test()
    end.

我运行这个: excript rmq.erl

此代码不起作用。看起来 spawn 不起作用。

如果我从 main 运行它,我的其余代码可以正常工作,函数 send 和 validate 可以正常工作(我已经对其进行了评论)。我做错了什么?

对不起,也许这是一个愚蠢的问题,但我是 erlang 的初学者。我已经尝试在互联网和书籍中搜索答案,但我失败了......

【问题讨论】:

    标签: erlang actor erlang-escript


    【解决方案1】:

    问题实际上不在于 spawn,而在于模块/escript 混乱。

    简而言之,即使您使用-module() 指令,escript 文件也不是真正的模块,不是从 Erlang VM 的角度来看的。它们被解释了,根本没有编译,绝对不能被“rmq:test()”之类的模块调用,或者在你的情况下通过spawn的动态模块调用。

    最简单的解决方案是将脚本与实际模块分开。在您的 rmq.es 中,您只需启动一些适当的模块:

    #!/usr/bin/env escript
    %%! -pz ../deps/amqp_client ../deps/rabbit_common ../deps/amqp_client/ebin ../deps/rabbit_common/ebin
    
    main(_) ->
      rmq:start().
    

    在模块rmq.erl:

    -module(rmq).
    -export([start/0, send/1, validate/0, test/0]).
    -include_lib("../deps/amqp_client/include/amqp_client.hrl").
    
    start() ->
        Pid = spawn(rmq, test, []),
    
        %% Pid = spawn(?MODULE, test, []),  %% works with macro too
    
        %% Pid = spawn(fun() -> test() end), <= I've tried this way too
        Pid ! s.
    
    test() ->
        receive
            s ->
                io:format("BAR ~n"),
                send(<<"esio">>),
                test();
            get ->
                validate(),
                test();
            _ ->
                io:format("FOO"),
                test()
        end.
    

    或者你可以在没有 escript 的情况下启动这个模块,像这样使用 -run 标志

    erl -pz deps/*/ebin -run rmq start 
    

    编辑关于编译问题

    您使用erlc command 编译您的模块。要编译使用erlc rmq.erl,它将在当前目录中生成rmq.beam 文件。但是惯例是将所有源文件保存在src 目录中,将所有编译文件保存在ebin 目录中,并且可以将运行脚本之类的东西放在顶层目录中。类似的东西:

    project
    |-- ebin
    |   |-- rmq.beam
    |
    |-- src
    |   |-- rmq.erl
    |
    |-- rmq.es
    

    假设您从project 目录运行所有shell 命令,从src 编译所有文件并将.beam 二进制文件放在ebin 使用erlc src/rmq.erl -o ebinerlc src/* -o ebin 在文档中你可以找到你-oflag的解释

    -o目录

    编译器应该放置输出文件的目录。如果未指定,则输出文件将放置在当前工作目录中。

    然后,编译后,您可以使用erl Erlang VMescript 运行您的代码(其中一种使用erl

    erl 运行已编译模块中的代码,为此他需要能够定位那些已编译的 *.ebin 二进制文件。为此,他使用代码路径,这是他将在其中搜索这些文件的主管列表。此列表自动包含标准库目录,当然您可以使用-pa 标志使用您自己的代码添加目录。

    -pa Dir1 Dir2 ...

    将指定目录添加到代码路径的开头,类似于code:add_pathsa/1。见代码(3)。作为 -pa 的替代方法,如果要在代码前添加多个目录并且这些目录具有公共父目录,则可以在 ERL_LIBS 环境变量中指定该父目录。见代码(3)。

    在您的情况下,它将是 erl -pa ebin,或者包含您可以使用 erl -pa ebin -pa deps/*/ebin 的所有部门的二进制文件。

    在您的 escript 的第二行中使用了完全相同的选项。 * 字符除外,它不会像在 shell 中那样扩展。在 escript 中,您必须分别提供每个依赖项的路径。但是包含-pa ebin 的想法保持不变。

    为了自动化和标准化这个过程工具,如创建的rebarerlang.mk(我推荐后者)。使用这些应该会对您的工作流程有所帮助。

    【讨论】:

    • 您好,谢谢您的回答,但我还是有问题。 escript:异常错误:未定义函数 rmq:start/0
    • 检查rmq.erl 模块是否编译为rmq.beam 没有错误。并确保二进制目录位于erl 路径中(带有-pa DIR-pz DIR)。
    • 我用:erlc -pa deps/*/ebin rmq.erl 编译并得到编译错误:文件没有扩展名:/home/esio/work/stacja/src/deps/rabbit_common/ebin 为什么如果我使用 erlc 则编译不起作用,如果我使用 escript 则编译有效?
    • erlcerl 之间的一些解释扩展了我的答案,以及在哪里使用哪些标志。
    • 感谢您的解释,但我的问题不同,我可以从问题中运行我的 erlang 脚本,但我无法编译...:/ Erlang 编译器无法识别我的 rabbitmq 依赖项。 .. :/ 我认为这不是我的问题...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-19
    • 1970-01-01
    • 2014-06-07
    • 1970-01-01
    • 2012-10-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多