C++并发编程框架Theron(1)——Actor模型介绍

时间:2022-02-13 17:58:25

1 说在前面的话


C++并发编程框架Theron(1)——Actor模型介绍

  Theron是近些年发展起来的一个非常不错的C++并发编程框架,最近有详细阅读Theron的相关资料,发现它思想非常有条理,结构很明朗,非常适合项目开发。其实Theron国内研究还是非常少的,目前还没有看到什么公开项目使用该框架。但是这并不代表Theron不够优秀,一件新鲜事物要被众人接受自然需要经受一段时间的考验,所以我觉得自己应该尽一点微薄之力来当一名小小的传播者。当然,这点抬手之举相比较原作者的贡献简直不值得一提,看了所有Theron官网上开源的资料与源码,你就会感叹为何有这么详细的解释与示例,这不正是传说中注释比源代码还要长的开源库吗?!作者的耐心与努力值得我们每一个人竖起大拇指。
  此外,我也需要点明,你只要有一些C/C++经验直接阅读Theron官网的资料也一定能有很多收获。所以我后面所有的博文也仅仅是起一个翻译与自己见解介绍的作用,如果你有什么困惑的地方,那你不妨直接去Theron官网查找相应估计会有更好的理解。好了,废话不多说,让我们进入正题吧。
2 Theron是什么

C++并发编程框架Theron(1)——Actor模型介绍
图1 线程运行图

  近些年来,多线程开发与分布式开发受到越来越多人的关注。但是有过多线程开发与调试的人都知道,它的复杂性,调试难,易出错,内存共享,消息同步足以令每个开发者望而生畏,每写一条语句都想放弃查实其他开发方案。如图1所示,一个线程在多线程系统中运行的状态是要考虑很多方面的,现实开发中的复杂度更是超乎我们的想象。
  当然,共享内存的多线程开发中锁机制的引入,一定程度上能够保证线程同步,逻辑一致性。但是锁机制带来的资源消耗,效率低下问题,甚至有时候带来死锁等等一系列问题,不但没有得到想要的,而且还失去更多。
  图2前两行图展示了锁带来堵塞的问题,每个时刻只能允许一个线程工作(解锁的钥匙每次只有一个人能拿到),如同只能允许一个人蹲马桶一样,后面的人必须排队,所以效率可想而知是很低的。

C++并发编程框架Theron(1)——Actor模型介绍
图2 共享内存的多线程锁机制的比喻

  而图2第三行展示了Theron框架基于Actor模型的工作机制,它们通过消息通信来多线程工作。那Actor模型这种消息通信的工作机制到底是怎样的呢?它的优势在哪里?这一篇博文我将主要围绕Actor模型来叩开Theron框架的大门。
3 Actor模型
 3.1 介绍
  Theron框架是一个基于Actor模型的并发编程的C++库。而Actor模型使得Theron框架可以直接有效的创建并行分布式的应用。Theron框架提供了很多轻量便携的函数接口API,可以使用在Linux,Windows,Mac,ARM和Matlab环境。
  我们知道面向对象编程中使用的是Object模型,宣扬一切皆是对象,数据+行为=对象。而Actor模型则认为一切皆是Actor,Actor模型内部的状态由自己的行为维护,外部线程不能直接调用对象的行为,必须通过消息才能激发行为,也就是使用消息传递机制来代替Object模型的成员方法的调用,这样就保证Actor内部数据只能被自己修改。Actor模型=数据+行为+消息。
  但是C++是一种面向对象的语言,所以最终还是通过类来封装这种actor的机制,多线程的实现也是依靠内存共享的多线程机制设计的,只不过这些事情Theron源码已经帮我们完成了,我们直接使用它给出的类接口即可。Theron框架的多线程基础支持pthreads,Windows threads,boost::thread和C++11 threads四种传统类型来构建。多说一嘴,因为Theron框架在C++中说到底还是通过类封装实现Actor模型的,自然我们直接通过类对象调用类中方法数据。但是为了保证Theron框架生态的完整性,并且真正体现actor模型的优越性,我们还是不要如此为好。
  基于Object模型与Actor模型区别如图3所示。

C++并发编程框架Theron(1)——Actor模型介绍
(a)
C++并发编程框架Theron(1)——Actor模型介绍
(b)
图3 基于Object机制与基于Actor机制的比较

  从图中可以看到,类A的对象成员方法调用类B的对象成员方法需要经过A::call调用B::called方法,然后等待B::called方法执行完成并且返回响应,最后A::call继续上次调用的地方后面执行下去。而在Actor模型中,Actor A先发送消息给Actor B,然后即刻就返回继续执行下面的程序,而Actor B中收到消息被唤醒和Actor A并行执行下去。
  至此,Actor模型就可以看出这种消息机制的线程调用最大好处是非阻塞的,多个线程可以同时并发进行,无需等待被调用方法执行完成返回消息的响应。当然,看到此处大家或许跟我一样有一点困惑的地方,即万一我们后面的程序需要立即使用它返回的响应消息怎么办呢?其实这也算Actor存在的一点不足之处,需要我们在设计多线程前考虑你的程序到底适不适合这种机制,后面我们会再详细描述。
 3.2 Actor模型与共享内存模型不同点
  Actor这种独立并发的模型,与另一种共享内存模型完全相反。Actor之间通过消息传递方式进行合作,相互线程独立。随着多核时代和分布式系统的到来,共享内存模型其实不适合开发的。我们以酒店厨房做菜为例,如图4所示。

C++并发编程框架Theron(1)——Actor模型介绍
图4 Actor模型与共享内存模型的比喻图示

  ①、单线程编程,如图4(a)——犹如酒店只有一个厨师员工(一个线程),所有菜按点菜顺序与工序完成到底就行;
  ②、共享内存的多线程编程,如图4(b)——犹如酒店厨房是由洗菜工,刀工,掌勺,服务员等(每个人是一个线程),他们之间的确能通过合作能比一个厨师完成所有工序要快。我们需要考虑的是菜相当于是他们共享的资源,每次只能一个人在对其做处理,虽然有多道菜品,但是总会在穿插间存在等待。
  ③、Actor模型的多线程编程,如图4(c)——犹如酒店有多个厨师,分别是川菜师傅,鲁菜师傅,徽菜师傅等等,他们只要接到客人点菜的需求,整个人独自完成相对应菜系的工序,之间不管对方师傅要干嘛,如此多线程工作就大大增加了效率,并且不存在互相等待资源的情况,做好了自己发消息给服务员端菜即可。
  这样我们就可以看出Actor模型异步消息传递来触发程序并行执行,虽然不如直接调用来的直接而方便,但是它可以让大量消息真正意义上同步执行。同时消息让Actor之间解耦,消息发出去之后执行成功与否,耗时多少等等只要没有消息传递回来,一切都不在与发送方有任何关联。这样也保证了,我们不需要在共享环境中与同步锁,互斥体等常用基础多线程元素打交道。
 3.3 Actor模型的优缺点
  优点没什么再需要阐述的了,上面已经介绍的很详细,下面我们来看看它到底存在什么缺点,了解缺点对我们什么时候使用Actor模型有很大的规划作用。
  ①、每个Actor虽然是独立运行的,但是一旦同时接收到多个消息,一次也仅能处理一条消息,也就是按消息队列处理;
  ②、Actor间异步执行,通过消息的传递实现协同。所以一个Actor内部出现执行错误很可能造成整个程序的错误——比如一个或多个其他Actors可能一直等待它反馈的消息。所以我们一定需要好好的处理Actors的例外事件,并且有容错机制及时反馈错误信息给其他等待中的Actors;
  ③、Actor模型也存在死锁问题,也有可能遇到它们之间互相等待消息,所以可以使用超时设定的功能打破这个死锁情况;
  ④、从上面的阐述我们也能窥探一二,并不是所有的应用开发都很好适合Actor模型。当且仅当我们可以将问题分为多个小模块,并且它们各自是独立运行的,仅个别地方需要交流。如果多线程间需要频繁互动,存在交叉的环境,其实Actor模型的效率优势也就不复存在了,也就失去了其消息调用并行执行的必要性。

4 小结 
  这篇博文主要还是开个Theron框架的开头,主要介绍Theron框架的基石Actor模型,至于Theron框架的使用,函数接口,各种小示例我也会在今后抽空一一介绍。Actor并发模型实际上在很多语言上已经有使用,但是此次Theron框架将其封装到C++开发上,真正给我们C++程序开发带来了很大的便利。
  以上是个人学习记录,由于能力和时间有限,如果有错误望读者纠正,谢谢!
  转载请注明出处:http://blog.csdn.net/FX677588/article/details/74359823


  参考文献:
  Theron框架官网http://www.theron-library.com/
  为什么Actor模型是高并发事务的终极解决方案?http://www.jdon.com/45728
  Actor模型的优缺点http://blog.chinaunix.net/uid-21712186-id-5000328.html
  详解Theron通过Actor模型解决C++并发编程的一种思维http://www.mahaixiang.cn/bcyy/954.html