首页 > 代码库 > 分布式异步消息框架构建笔记5——如何避开并行编程中的数据共享陷阱
分布式异步消息框架构建笔记5——如何避开并行编程中的数据共享陷阱
任何多线程/并行/分布式都会面临一个问题,“数据状态共享"。
有经验的开发者会说,要想正确有效的避开避开状态共享,那么就应该别用任何状态共享。
虽然不得不说,这是一个不错的建议,但是没有状态共享,你需要如何才能知道非本地数据的状态?
也许你会说使用消息,使用消息来处理,那么我们丑陋的回调金字塔应该叠的更高了。
不得不说这是一个解决办法,但是为了保持状态不被修改,那么我们还得在远程申请一个写入锁,防止数据被别的任务所修改。
那么流程就是 申请锁-》请求某个消息状态-》释放锁
假设我们一个简单的交易任务,服务A上的玩家1准备和服务器B上的玩家2准备交易。
玩家A需要付出100个金币,玩家B需要把背包里的一把刀给玩家A。
非分布式简单非常简单
if(玩家A.金币>100 && 玩家B.拥有(刀)){
玩家A.金币-=100;
玩家A.获得(刀);
玩家B.失去(刀);
}
防止逻辑失败,如果有个事务出错后回滚一下,这个逻辑就完美了,简单有效.
那么如果是分布式这就显得比较麻烦了, 试着写一个同步办法,远程调用比较费时间,但是至少能正常工作.
sv1.锁定(玩家A)
sv2.锁定(玩家B)
if(sv1.玩家A.金币>100 && sv2.玩家B.拥有(刀)){
sv1.玩家A.金币-=100;
sv2.玩家A.获得(刀);
sv3.玩家B.失去(刀);
}
sv1.释放(玩家A)
sv2.释放(玩家B)
这个时候应该请出异步来优化这次调用了..
sv1.锁定(玩家A,(){
sv2.锁定(玩家B,(){
if(sv1.玩家A.金币>100 && sv2.玩家B.拥有(刀)){
sv1.玩家A.金币(-100,(){
sv2.玩家A.获得(刀,(){
sv3.玩家B.失去(刀,(){
sv1.释放(玩家A,(){
sv2.释放(玩家B,(){
});
});
);
});
});
}
})
});
我好想确定自己是不是来搞笑的..
如果保持之前设想的逻辑:
1.所有对单个对象执行都是单线程的.(数据访问唯一)
2.当对象不在本地,提交到远程执行,并返回结果.
3.获得结果后,继续在本地执行
交易发起会在玩家A这方发起,我们克隆一个玩家B的镜像,并且标记一个有效时间.
所有的修改都有操作标记.然后在玩家A和玩家B的逻辑都在玩家A线程上执行.
但是,因为我们操作的镜像,并不影响原有数据.
操作完逻辑后,并不算成功,我们需要获得操作完成后玩家A/B与交易之前的差异数据
如何获得参考之前的一篇文章,然后到了我们真正要写入的时候.
玩家A置等待锁(也就是暂时不执行别的请求),远程请求玩家B的写入.
完成后,玩家A写入. 释放等待锁.
写入瞬间有一个防误操作的锁,其他时间都不影响其他任何操作.
如果远程服务器在交易发起的瞬间被,修改,那么写入失败,只要丢弃双方差异数据并释放锁即可.
从以上逻辑我们可以看出
1.数据访问唯一让我们无需担心执行过程中被修改,也就是我们可以推算出任何非多步的操作都是成功的.(方法中不包含远程方法,必然会执行成功)
2.线程切换机制,保证我们在编写代码时运行位置的正确性,因为你无需判断是否需要远程执行,仅仅都通过代理调用即可.
3.除非事务,保证双方的操作必须成功,否则,无需任何锁,也就是安全的跨服操作.
分布式异步消息框架构建笔记5——如何避开并行编程中的数据共享陷阱