其实一开始是看到mailinglist有对于这个的讨论,我就加上了自己的理解扩展概括了一下,存此。

 

首先我们要确定erlang中像全局变量这种东西是不存在的,如果你非要保存一个全局的状态也并非不可以实现。

 

1.使用ets,将其指定为public, 这个不用多说了,随取随用。

2.将其保存在一个固定进程中,无论读取都对其发出message进行操作,这个本质上并不违反以进程为中心的原则。

3.使用application:set_env, application:get_env(但我看了一下application_controller的源码,其实本质上也只是往一个叫ac_tab的public ets中读写,相当于第一条和第二条的结合使用)

 

除此以外,开源项目mochiweb中的mochiglobal模块也实现的类似的功能,但本质并不相同,接下来,我贴出代码,来分析一下原理。

%% @author Bob Ippolito <bob@mochimedia.com>
%% @copyright 2010 Mochi Media, Inc.
%% @doc Abuse module constant pools as a "read-only shared heap" (since erts 5.6)
%%      <a href="http://www.erlang.org/pipermail/erlang-questions/2009-March/042503.html">[1]</a>.
-module(mochiglobal).
-author("Bob Ippolito <bob@mochimedia.com>").
-export([get/1, get/2, put/2, delete/1]).

-spec get(atom()) -> any() | undefined.
%% @equiv get(K, undefined)
get(K) ->
    get(K, undefined).

-spec get(atom(), T) -> any() | T.
%% @doc Get the term for K or return Default.
get(K, Default) ->
    get(K, Default, key_to_module(K)).

get(_K, Default, Mod) ->
    try Mod:term()
    catch error:undef ->
            Default
    end.

-spec put(atom(), any()) -> ok.
%% @doc Store term V at K, replaces an existing term if present.
put(K, V) ->
    put(K, V, key_to_module(K)).

put(_K, V, Mod) ->
    Bin = compile(Mod, V),
    code:purge(Mod),
    {module, Mod} = code:load_binary(Mod, atom_to_list(Mod) ++ ".erl", Bin),
    ok.

-spec delete(atom()) -> boolean().
%% @doc Delete term stored at K, no-op if non-existent.
delete(K) ->
    delete(K, key_to_module(K)).

delete(_K, Mod) ->
    code:purge(Mod),
    code:delete(Mod).

-spec key_to_module(atom()) -> atom().
key_to_module(K) ->
    list_to_atom("mochiglobal:" ++ atom_to_list(K)).

-spec compile(atom(), any()) -> binary().
compile(Module, T) ->
    {ok, Module, Bin} = compile:forms(forms(Module, T),
                                      [verbose, report_errors]),
    Bin.

-spec forms(atom(), any()) -> [erl_syntax:syntaxTree()].
forms(Module, T) ->
    [erl_syntax:revert(X) || X <- term_to_abstract(Module, term, T)].

-spec term_to_abstract(atom(), atom(), any()) -> [erl_syntax:syntaxTree()].
term_to_abstract(Module, Getter, T) ->
    [%% -module(Module).
     erl_syntax:attribute(
       erl_syntax:atom(module),
       [erl_syntax:atom(Module)]),
     %% -export([Getter/0]).
     erl_syntax:attribute(
       erl_syntax:atom(export),
       [erl_syntax:list(
         [erl_syntax:arity_qualifier(
            erl_syntax:atom(Getter),
            erl_syntax:integer(0))])]),
     %% Getter() -> T.
     erl_syntax:function(
       erl_syntax:atom(Getter),
       [erl_syntax:clause([], none, [erl_syntax:abstract(T)])])].

其实就是每次调用put方法时,使用erl_syntax动态组合成一个abf, 使用code:load_binary加载新代码。

假设我们这样使用put方法

mochiglobal:put(a, 1).

实际生成了一个新模块'mochiglobal:a'

内容大致如下:

-module('mochiglobal:a').
-export([term/0]).
term()
->   1.

我们来看一下实际操作时的效果:

上面可以看到在put之后,内存中加载了"mochiglobal:a.erl"文件

然后我们看一下'mochiglobal:a'的模块属性

上面可以清晰的看出该模块有一个term/0的导出方法。

 

接下来的操作可以看出:

get时即正常的调用内存中的模块term函数。

而delete时就是简单的从内存中将编译加载的模块移除而已。

 

 posted on 2013-02-19 19:06  文武双全大星星  阅读(1804)  评论(0编辑  收藏  举报