Mnesia 交易正是为你做这件事。这就是事务的用途,除非你做了肮脏的操作。因此,只需将写入和读取操作放入一个事务中,mnesia 就会完成其余操作。一个事务中的所有操作都作为一个原子操作完成。 Mnesia 事务隔离级别有时称为“可串行化”,即最强的隔离级别。
Edit:
看来你错过了关于 Erlang 并发进程的一个重要观点。 (公平地说,这不仅在 Erlang 中如此,而且在任何真正的并发环境中都是如此,当有人争论时,这不是真正的并发环境。)除非您进行一些同步,否则您无法区分哪个操作首先发生,哪个操作第二个发生。实现此同步的唯一方法是使用消息传递。关于 Erlang 中的消息,您只能保证一件事,即从一个进程发送到另一进程的消息的顺序。这意味着当您发送两条消息时M1
and M2
从过程A
处理B
他们以相同的顺序到达。但如果你发消息M1
from A
to B
和留言M2
from C
to B
他们可以按任何顺序到达。只是因为您如何知道您先发送了哪条消息?如果你发消息那就更糟糕了M1
from A
to B
进而M2
from A
to C
什么时候M2
到达C
send M3
from C
to B
你没有保证这一点M1
到达B
before M3
。即使在当前实施中,它也会发生在一台虚拟机中。但你不能依赖它,因为它没有得到保证,甚至在下一个版本的 VM 中也可能会因为不同调度程序之间的消息传递实现而发生变化。
它说明了并发进程中的事件排序问题。现在回到 mnesia 事务。 Mnesia 交易必须无副作用fun
。这意味着可能没有任何消息从交易向外发送。所以你无法判断哪个事务首先开始以及何时开始。您唯一能判断交易是否成功的事情是,他们命令您只能根据其效果来确定。当你考虑到这一点时,你微妙的澄清就毫无意义了。即使在事务实现中是一个一个地读取一个键,一个事务也会以原子操作的方式读取所有键,并且您的写操作也将作为原子操作执行。您无法判断在第一个事务中读取第一个密钥后是否发生了对第二个事务中的第四个密钥的写入,因为从外部无法观察到它。两个事务都将作为单独的原子操作按特定顺序执行。从外部角度来看,所有密钥都将在同一时间点被读取,这是记忆力强制执行的工作。如果您从事务内部发送消息,则违反了 mnesia 事务属性,并且它会表现得很奇怪,您不会感到惊讶。具体来说,这个消息可以发送多次。
Edit2:
如果您启动其中的几个进程,您会发现非常
很快,a、b 和 c 就处于其值之和不等于 10 的状态。
我很好奇为什么你认为会发生这种情况或者你测试过它?向我展示你的测试用例,我将展示我的测试用例:
-module(transactions).
-export([start/2, sum/0, write/0]).
start(W, R) ->
mnesia:start(),
{atomic, ok} = mnesia:create_table(test, [{ram_copies,[node()]}]),
F = fun() ->
ok = mnesia:write({test, a, 10}),
[ ok = mnesia:write({test, X, 0}) || X <-
[b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z]],
ok
end,
{atomic, ok} = mnesia:transaction(F),
F2 = fun() ->
S = self(),
erlang:send_after(1000, S, show),
[ spawn_link(fun() -> writer(S) end) || _ <- lists:seq(1,W) ],
[ spawn_link(fun() -> reader(S) end) || _ <- lists:seq(1,R) ],
collect(0,0)
end,
spawn(F2).
collect(R, W) ->
receive
read -> collect(R+1, W);
write -> collect(R, W+1);
show ->
erlang:send_after(1000, self(), show),
io:format("R: ~p, W: ~p~n", [R,W]),
collect(R, W)
end.
keys() ->
element(random:uniform(6),
{[a,b,c],[a,c,b],[b,a,c],[b,c,a],[c,a,b],[c,b,a]}).
sum() ->
F = fun() ->
lists:sum([X || K<-keys(), {test, _, X} <- mnesia:read(test, K)])
end,
{atomic, S} = mnesia:transaction(F),
S.
write() ->
F = fun() ->
[A, B ] = L = [ random:uniform(10) || _ <- [1,2] ],
[ok = mnesia:write({test, K, V}) || {K, V} <- lists:zip(keys(),
[10-A-B|L])],
ok
end,
{atomic, ok} = mnesia:transaction(F),
ok.
reader(P) ->
case sum() of
10 ->
P ! read,
reader(P);
_ ->
io:format("ERROR!!!~n",[]),
exit(error)
end.
writer(P) ->
ok = write(),
P ! write,
writer(P).
如果它不起作用,那将是一个非常严重的问题。有一些重要的应用程序,包括依赖它的支付系统。如果您的测试用例显示它已损坏,请报告错误[电子邮件受保护] /cdn-cgi/l/email-protection