2016-08-21 83 views
0

我有用于运行简单银行帐户的此代码。 有两种存款方法和一种用于初始化平行存款到账户的测试方法。Erlang - 文件之间的传输 - MUTEX

有人能帮我实现一个功能,在2个账户之间转移资金,并添加互斥锁来防止死锁吗?

-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

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

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

然后您将不得不定义用于存入,检查,撤销和传输用例的消息序列。使用同步和异步协议(我猜有些超时)可以保证数据的一致性,避免死锁。

Erlang代码不同于C++或面向对象的Java代码。 “方法”(实际上全部在您的账户/ 1函数的接收块中实现)在账户过程中执行:没有并发担心存在。接口函数(如在客户端进程中执行的撤销/ 1)也是如此。

说了这样的话,您可以看到,由于存款账户的每个角色(界面和账户余额管理)都有明确的分离,所以存款账户2/1的代码是安全的,但是存款账户1/1是不安全的,余额在客户端进程中使用2次单独访问服务器(帐户)进程来更新余额。如果2个请求在同一时间快到了,你可能有一个平衡的错误:

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

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

account transfer use case