Erlang - 在文件之间传输 - MUTEX

时间:2016-08-21 21:15:35

标签: concurrency erlang mutex

我有运行简单银行帐户的代码。 有两种存款方法和一种初始化账户平行存款的测试方法。

有人会帮我实现一个在两个帐户之间转账的功能,并添加互斥锁以防止死锁吗?

-module(bank).
-export([account/1, start/0, stop/0, deposit1/1, deposit2/1, get_bal/0, set_bal/1, withdraw/1]).

%test

-export ([test/3,user/3]).

account(Balance) ->
receive
    {set, NewBalance} ->
        account(NewBalance);
    {get, From} ->
        From ! {balance, Balance},
        account(Balance);
    {deposit, Amount, From} ->
        NewBalance = Balance + Amount,
        From ! {deposit, Amount, NewBalance},
        account(NewBalance);
    {withdraw, Amount, From} when Amount > Balance ->
        From ! {error, {insufficient_funds, Amount, Balance}},
        account(Balance);
    {withdraw, Amount, From} ->
        NewBalance = Balance - Amount,
        From ! {withdrawal, Amount, NewBalance},
        account(NewBalance);    
    stop -> ok
end.





start() ->
    Account_PID = spawn(bank, account, [0]),
    register(account_process, Account_PID).

stop() ->
    account_process ! stop,
    unregister(account_process).

set_bal(B) ->
    account_process ! {set, B}.

get_bal() ->
    account_process ! {get, self()},
    receive
        {balance, B} -> B
    end.

deposit1(Amount) ->
    OldBalance = get_bal(),
    NewBalance = OldBalance + Amount,
    set_bal(NewBalance).

deposit2(Amount) when Amount > 0 ->
    account_process ! {deposit, Amount, self()},
    receive
        {deposit, Amount, NewBalance} ->
            {ok, NewBalance}
    end.

withdraw(Amount) when Amount > 0 ->
    account_process ! {withdraw, Amount, self()},
    receive
        {withdrawal, Amount, NewBalance} ->
            {ok, NewBalance};
        Error ->
            Error
    end.


test(Nbuser, Nbdeposit, Method) ->
    start(),
    done = spawn_users(Nbuser,Nbdeposit,Method,self()),
    receive_loop(Nbuser),
    Res = (get_bal() == Nbdeposit*Nbuser),
    stop(),
    Res.

spawn_users(0,_Nbdeposit,_Method,_Pid) -> done;
spawn_users(Nbuser,Nbdeposit,Method,Pid) ->
    spawn(?MODULE,user,[Nbdeposit,Method,Pid]),
    spawn_users(Nbuser-1,Nbdeposit,Method,Pid).

receive_loop(0) -> done;
receive_loop(N) ->
    receive
        end_deposit -> receive_loop(N-1)
    end.

user(0,_,Pid) ->
    get_bal(), % to be sure that with method deposit1, the last set_bal is processed
    Pid ! end_deposit;
user(N,Method,Pid) ->
    ?MODULE:Method(1),
    user(N-1,Method,Pid).

1 个答案:

答案 0 :(得分:1)

您的帐户流程管理一个帐户,因为它是一个注册过程,您无法使用此代码管理多个帐户。

首先,您需要决定是否扩展帐户/ 1功能,以便在一个流程中管理多个帐户,或者您是否要创建一个银行流程来管理多个"单个帐户流程"例如,通过帐号和/或所有者以及与其pids的关联来识别。

然后,您必须定义用于存放,检查,撤销和转移用例的消息序列。使用同步和异步协议(我猜一些超时)可以保证数据的一致性,并避免任何死锁。

Erlang代码与C ++或java面向对象代码不同。 "方法" (实际上所有在您的account / 1函数的接收块中实现的)都在帐户进程中执行:没有并发性可以担心。对于在客户端进程中执行的接口函数(例如withdraw / 1)也是如此。

说到这一点,你可以看到deposit2 / 1的代码是安全的,因为它清楚地分离了每个角色(接口和帐户余额管理),但是deposit1 / 1是不安全的,因为接口正在对在客户端进程中,使用对服务器(帐户)进程的2次单独访问来更新余额。如果同时发出2个请求,则可能会出现余额错误:

race condition with the deposit1 method. diagram made with "plantuml"

看起来这个问题是关于家庭作业或自学,所以我让你找到一个解决方案。我希望这可以帮到你。我在这里举了一个例子,每个帐户使用一个进程,虽然我不认为它是一个很好的架构;它应该管理死锁。

account transfer use case