Theron是一个基于Actor Model的轻量级C++并行库(Theron is a fast, portable, lightweight C++ concurrency library based on Actor Model)。
在分析Theron源代码前,先简单说一说Actor Model到底是一个什么东西。
Actor Model
Actor Model是一种并发模型,详细的信息你可以在wiki上找到。如果你觉得英文比较难理解,也可以看看老赵的博客,他写了3篇关于Actor Model的博文(1,2,3)。可以说,看完这三篇文章,你应该能够对Actor Model建立一个直观的认识。
我个人觉得并行编程的模型强调了一点,不要使用简单的线程来解决并发的问题,因为不容易处理好锁与锁之间的关系。要从整体业务上来考虑问题,以一个一个关联不是很紧密的流程为基础来进行并发。也就是说,以task为基础进行并发。
说到这,前戏算是完了,我们进入正题。。。。
在Theron中,或者说在Actor Model中有几个重要的概念,它们分别是:
- Actor
- Message
- Address
- Mailbox
Actor就不多说了。在Actor Model中,万事万物皆actor。和面向对象中强调万事万物皆对象是一样的。
Message就是消息吧。在Actor Model中,actor与actor之间通过消息进行通信。那么actor如何将指定的消息发送给它想通知的其他actor呢?靠Address。
Address就是地址,更严格地说是mailbox的地址。Actor和actor只能通过地址相互通信。因此,如果两个actors相互不知道对方的地址,那么它们就无法相互发送消息。接收消息的actor可以从message中提取到发信actor的地址。
Mailbox就是邮箱,用来存放收到的消息。
所以,Actor Model可以形象地理解成人和人之间相互寄信。人就是actor,信就是message,家庭地址就是address,邮箱就是mailbox,信封上有寄信人的地址。也正因为这种类比,我们可以想象:
- 不同actor之间是并行执行的
- Actor可以顺序执行收到的消息,也可以并行执行。一般来说,顺序执行比较容易实现
在Theron中,除了上面4个重要的类之外,还有一个类也很重要,就是Framework类。事实上他有点像邮局。
既然Theron是一个并发库,那势必还要有线程相关的类。直接和线程相关的有4个,他们分别是:
- Thread
- ThreadContext
- WorkerThreadStore
- ThreadPool
间接关联的有一个:
- ProcessorContext
我们先从线程相关的类开始说起。
Thread and ThreadPool
Thread class
根据运行环境的不同,Theron通过宏定义区别出了3种实现Thread类的方法,分别是:
- 基于Win32 API的实现
- 基于boost::thread的实现
- 基于C++11 std::thread的实现
你要定义一个具体的业务处理函数,该函数会在特定实现中的线程里被调用。
主要的几个成员函数是:
- Start(EntryPoint entryPoint, void* context)
- Join
ThreadContext struct
这个结构是ThreadPool中的内部被定义的。所以可以知道它和ThreadPool的关系要比Thread类和ThreadPool的关系来得更紧密。事实上也是这样的。ThreadPool所管理的具体线程事实上是通过ThreadConext来进行的。所以确切地说,线程池中的线程在这里指ThreadContext更恰当些。
先来看下它的具体定义。
1: struct ThreadContext
2: {
3: WorkerContext* mWorkerContext;
4:
5: WorkQueue* mWorkQueue;
6: uint32_t mNodeMask;
7: uint32_t mProcessorMask;
8: bool mRunning;
9: bool mStarted;
10: Thread* mThread;
11: };
这里有2个东西可能比较困惑。一是,WorkerContext;二是,WorkQueue。除此以外,应该没有不难理解的东西。WorkerContext和WorkQueue暂时先搁置下,等讲到ThreadPool线程池的时候我们再来说这两个东西。
ThreadPool class
说到ThreadPool,我想大家应该都不怎么陌生。在操作系统级别都提供了线程池相关的函数。这里需要强调一点的是,Theron中线程池中的线程不是Thread而是ThreadContext。为什么这么说?等到说Framework类的时候,你就更清楚了。
ThreadPool是一个模版类。
1: template <class WorkQueue, class WorkProcessor, class WorkerContext>
2: class ThreadPool;
这个类在Framework类中被这样使用:
1: typedef ThreadSafeQueue<Mailbox> WorkQueue;
2: typedef ThreadPool<WorkQueue, WorkItem, WorkerThreadStore> ThreadPool;
所以,ThreadContext中的WorkQueue就是ThreadSafeQueue<Mailbox>类型,WorkerContxt就是WorkerThreadStore类型。WorkerThreadStore会在后面介绍,现在你可以认为他是工作线程保存相关信息的一个storage。
ThreadPool提供了几个重要的成员函数。
CreateThread(ThreadContext* threadContext)
创建一个Thread对象并把句柄保存到threadContext->mThread中。
StartThread(ThreadContext* threadContext, WorkQueue* workQueue, …)
启动线程。总共4个参数,这里罗列了前两个最重要的。
StartThread将workQueue保存到threadContext->mWorkQueue中,然后通过threadContext->mThread->Start启动线程,在线程中处理workQueue中的mailbox。
传递给Start函数的第一个参数EntryPoint是这样定义的:
1: void ThreadEntryPoint(void* context)
2: {
3: ThreadContext* threadContext(reinterpret_cast<ThreadContext*>(context));
4:
5: threadContext->mStarted = true;
6:
7: uint32_t backoff(0);
8: while (threadContext->mRunning)
9: {
10: if (typename WorkQueue::ItemType* item = threadContext->mWorkQueu->Pop())
11: {
12: WorkProcessor::Process(item, threadContext->mWorkerContext);
13: backoff = 0;
14: }
15: else
16: {
17: Utils::Backoff(backoff);
18: }
19: }
20: }
ItemType就是Mailbox。WorkProcessor在这里是WorkItem类。至于WorkItem::Process到底做了什么事情,还是留等后面我们对Theron了解更深入了之后再进行深入分析。这里你只要知道线程池道理这里就是逐一处理Mailbox里的信息了。
另外,这里还要注意的一个点是这里的while是一个死循环。所以当线程池把线程启动之后,除非特定操作,不然这个线程是不会退出的。
线程池类还提供了StopThread和DestroyThread两个函数,这里就不多做介绍了。
WorkerThreadStore and ProcessorContext
这两个结构放到后面再议。
说完了线程相关的内容,我们开始介绍Theron中和Actor Model概念有密切联系的几个类。先说Framework。
先到这里。。。未完待续。。。