Skynet模块
Actor在Skynet中被称为模块,每个模块都是由用户逻辑和内部框架逻辑组成。用户逻辑相当于模块的皮囊,在框架中使用skynet-module
对象表示,实现在skynet-src/skynet_module.c
中,代表一个动态库。Skynet的模块是一个动态链接库,可以生成若干个服务实例。服务所需要的函数是从动态链接库中取出来的。是以函数指针的形式取出来然后赋值的。
Actor模型的特点
- 系统以actor为单位持有资源,包括CPU、内存、对象。
- actor与actor之间不共享资源,只能通过邮箱互发消息。
- 不同的actor处理消息是并发的
- 对于每个actor可能在调度状态和非调度状态,调度状态下依次处理邮箱中的每个消息。
Skynet模块与服务之间的关系是什么样的呢?
Actor与死锁
用户A操作流程:
- 开始事务
- 访问表1
- 访问表2
- 提交事务
用户B操作流程:
- 开始事务
- 访问表2
- 访问表1
- 提交事务
如果用户A和用户B的两个事务同时发生,用户A锁着了表1未释放,因为整个事务未完成,当正在准备访问表2的时候,用户B的事务锁着了表2,因为整个事务也没有完成。当用户B正准备访问表1时,表1被用户A的事务给锁住了,此时只能等待用户A事务释放,而用户A此时正在等待用户B释放表2,因此陷入了无限等待,这也就是死锁(dead lock)。
那么问题出在哪里呢?其实无论是使用数据库锁还是多线程,共同的思路是将数据喂给线程,如同计算机是一套加工流水线,数据作为原料投入流水线,流水线出来后就成为成品。这套模式的前提是数据是被动的,流水线自身是不复杂的,因为没有自身业务逻辑要求。如果数据自身要求有严格的一致性,也就是事务机制,此时数据就不能被动被加工,要让数据自身有能力保护以实现自己的一致性。转变下思路,让数据自身有行为维护自己的一致性,才能真正安全的实现事务。
Actor模型的原理简单说来就是”Actor模型=数据+行为+消息“,Actor模型内部的状态由自身维护,外部线程不能直接调用对象的行为,必须通过消息才能激发行为,这样就能保证Actor内部数据只有被自己修改。
转账是典型符合该问题的案例,Actor的解决思路:...