2016-08-21 9 views
0

私は単純な銀行口座を運営するためのこのコードを持っています。 2つの入金方法と、口座への並行入金を初期化するためのテスト方法があります。Erlang - ファイル間の転送 - MUTEX

誰かが2つのアカウント間でお金を振り替える機能を実装するのに役立つでしょうか?デッドロックを防ぐために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

アカウントのプロセスは、登録プロセスであるとして、あなたはより多くの1つのアカウントよりも、このコードで管理することはできません、1つのアカウントを管理します。

複数のアカウントを1つのプロセスで管理するためにアカウント/ 1機能を拡張するかどうかを決定する必要があります。たとえば、複数の「単一アカウントプロセス」を管理する銀行プロセスを作成する場合口座番号および/または所有者とその関連団体とのpidへのリンク。

次に、ユースケースの入金、確認、引き出し、転送に使用するメッセージシーケンスを定義する必要があります。同期と非同期のプロトコル(と私はいくつかのタイムアウトと思う)を使用して、データの整合性を保証し、デッドロックを回避することが可能になります。

Erlangコードは、C++またはJavaオブジェクト指向コードとは異なります。 "メソッド"(アカウント/ 1関数の受信ブロックに実際に実装されているすべてのメソッド)は、アカウントプロセスで実行されます。恐れのある同時実行はありません。クライアントプロセスで実行されるwithdraw/1などのインタフェース関数についても同じです。

これは、各ロール(インタフェースと勘定残高管理)が明確に分離されているため、deposit2/1のコードは安全ですが、インタフェースが操作を実行しているため、deposit1/1は安全ではないことがわかりますクライアントプロセスの残高は、サーバー(アカウント)プロセスへの2つの別個のアクセスを使用して残高を更新します。 2つの要求が同時に来ている場合は、バランスの誤差を有していてもよい:

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

この質問は宿題や自習についてですので、私はあなたが解決策を見つけるてみましょうように見えます。私はこれがあなたを助けることを望む。ここでは、アカウントごとに1つのプロセスを使用した例を示しますが、それは優れたアーキテクチャではないと思います。デッドロックを管理する必要があります。

account transfer use case