Erlang - 在文件之间传输 - MUTEX
Erlang - Transfer between files - 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).
您的账户进程管理一个账户,因为它是一个注册进程,您不能使用此代码管理多个账户。
您首先需要决定是否要扩展 account/1 功能以在单个流程中管理多个帐户,或者您是否要创建一个银行流程来管理多个 "single account processes"例如,帐号 and/or 所有者及其 pids 的关联。
然后您将必须定义用于存款、支票、取款和转账用例的消息序列。使用同步和异步协议(我猜还有一些超时)可以保证数据的一致性,避免死锁。
Erlang 代码不同于 C++ 或 java 面向对象的代码。 "methods"(实际上都是在 account/1 函数的接收块中实现的)在帐户进程中执行:那里没有并发性。对于在客户端进程中执行的接口函数,如 withdraw/1 也是如此。
说到这里,你可以看到deposit2/1的代码是安全的,因为它对每个角色(接口和账户余额管理)进行了明确的分离,但是deposit1/1是不安全的,因为接口正在对余额进行操作,在客户端进程中,使用 2 个单独访问服务器(帐户)进程来更新余额。如果同时有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).
您的账户进程管理一个账户,因为它是一个注册进程,您不能使用此代码管理多个账户。
您首先需要决定是否要扩展 account/1 功能以在单个流程中管理多个帐户,或者您是否要创建一个银行流程来管理多个 "single account processes"例如,帐号 and/or 所有者及其 pids 的关联。
然后您将必须定义用于存款、支票、取款和转账用例的消息序列。使用同步和异步协议(我猜还有一些超时)可以保证数据的一致性,避免死锁。
Erlang 代码不同于 C++ 或 java 面向对象的代码。 "methods"(实际上都是在 account/1 函数的接收块中实现的)在帐户进程中执行:那里没有并发性。对于在客户端进程中执行的接口函数,如 withdraw/1 也是如此。
说到这里,你可以看到deposit2/1的代码是安全的,因为它对每个角色(接口和账户余额管理)进行了明确的分离,但是deposit1/1是不安全的,因为接口正在对余额进行操作,在客户端进程中,使用 2 个单独访问服务器(帐户)进程来更新余额。如果同时有2个请求过来,可能会出现余额错误:
看来这道题是关于作业或者自学的,所以让你自己想办法。我希望这可以帮助你。我在这里举了一个使用每个帐户一个进程的示例,尽管我认为这不是一个好的架构;它应该管理死锁。