Theron 是一个用于并发编程的 C++ 库(http://www.theron-library.com/),通过 Theron 我们可以避免多线程开发中各种痛处,例如:共享内存、线程同步。Theron 通过 Actor 模型向我们展示了另一种思维。
本文不是Theron的入门理解教程,而是重点理解Theron三个方面的概念,包括Unhandled messages,Message registration,以及remote actor,分别对应翻译为:未处理消息,消息注册,以及分布式actor
Unhandled Messages
一个actor可以注册任意多个消息处理函数(handler),各个函数可以用来接收并处理不同类型的消息。如果当前接收到的消息类型,没有任何handler能够处理,那怎么办?默认情况下,消息将不会被处理,而是由回滚处理函数(fallback handler)发出一个错误。这种做法是为了帮助捕捉所谓的unhandled message。
我们能够重载actor对unhandled message的默认处理函数,取而代之的是用户自定义的一个特殊的默认消息处理函数(special default message handler)。一旦该处理函数在actor中被注册了,默认的handler将能够处理任何之前actor无法识别的消息类型,如下代码所示:
// A simple actor that prints out strings it receives.
class Printer : public Theron::Actor
{
public:
Printer(Theron::Framework &framework) : Theron::Actor(framework)
{
// Register the handler for string messages.
RegisterHandler(this, &Printer::Print);
// Register the default handler for all other kinds of messages.
// If we don't register our own default handler then the 'default' default
// handler will be used, which asserts on receiving an unhandled message.
SetDefaultHandler(this, &Printer::DefaultHandler);
}
private:
void Print(const std::string &message, const Theron::Address from)
{
printf("%s\\n", message.c_str());
// Send the message back for synchronization.
Send(message, from);
}
// Default handler which handles messages of unrecognized types.
// This is a 'blind' handler which takes the unhandled message as raw data.
void DefaultHandler(
const void *const data,
const Theron::uint32_t size,
const Theron::Address from)
{
DumpMessage(data, size, from);
}
};
其中,默认的消息处理函数的主要问题是,在定义上,它们是无法知道它们要处理的消息的类型的。在此例中,我们采用了一种 blind handler的方式处理这些未知的消息类型,能够得到指向数据的指针和该数据类型的大小。此例中调用的DumpMessage()实现了将消息的内容导出的一个功能:
static void DumpMessage(
const void *const data,
const Theron::uint32_t size,
const Theron::Address from)
{
printf("Unhandled %d byte message sent from address %d:\n",
size,
from.AsInteger());
// Dump the message as hex data.
if (data)
{
const char *const format("[%d] 0x%08x\n");
const unsigned int *const begin(reinterpret_cast<const unsigned int *>(data));
const unsigned int *const end(begin + size / sizeof(unsigned int));
for (const unsigned int *word(begin); word != end; ++word)
{
printf(format, static_cast<int>(word - begin), *word);
}
}
}
尽管默认的消息处理函数很有用,但是每次都需要在actor中定义并注册。因此,这种处理函数一般用于特殊的actor(special actor)中,并不会用在所有的actor。
在框架层次存在一种类似于该特殊消息处理函数的机制,我们称之为fallback handlers。每个框架实例都注册了单个fallback handler,和actor默认的handler完全一样,但是只在以下两种情况:1)当消息发送至一个没有actor注册过的地址时;2)当消息发送给一个actor但是没有被该actor接收时,处理消息的时候直接执行一次回滚操作。
为了定义一个fallback handler,我们需要编写一个handler类,使得该handler的实例对象具备fallback handler,而且能够作为记录失败消息(log failed message)的地方。
// A handler object that reports any undelivered or unhandled messages.
class FallbackHandler
{
public:
// Fallback handler which handles any messages not handled by an actor.
// This is a 'blind' handler which takes the unhandled message as raw data.
void Handle(
const void *const data,
const Theron::uint32_t size,
const Theron::Address from)
{
DumpMessage(data, size, from);
}
};
这里有一个主程序例子显示了同时使用默认消息处理函数和fallback handler的情况:
int main()
{
Theron::Receiver receiver;
Theron::Address printerAddress;
// Create a framework and register a fallback handler with it.
Theron::Framework framework;
FallbackHandler fallbackHandler;
framework.SetFallbackHandler(&fallbackHandler, &FallbackHandler::Handle);
{
// Create a printer actor within a local scope and remember its address.
Printer printer(framework);
printerAddress = printer.GetAddress();
// Send the printer a message of a type which it doesn't handle.
// This message reaches the printer but is handled by its default handler.
framework.Send(103, receiver.GetAddress(), printerAddress);
// Send the printer a string message to show that it actually works.
framework.Send(std::string("hit"), receiver.GetAddress(), printerAddress);
// Wait for the reply to the handled string message, for synchronization.
receiver.Wait();
}
// Send a string message to the printer's address, which is now stale.
// This message never reaches an actor so is handled by the fallback handler.
framework.Send(std::string("miss"), receiver.GetAddress(), printerAddress);
}
在这个例子中,我们创建了一个printer actor,并在主程序的最后,销毁了该actor。这种方式导致最后发送的消息去了一个未知的地址空间,因此违反了前面的第(1)中情况,此时消息将会被fallback handler处理。而第一次发送的消息的类型是被printer actor的default handler接受处理的。
Message registration
Theron中允许注册消息类型,注册的函数和头文件如下所示:
#include <Theron/Register.h>
THERON_REGISTER_MESSAGE(std::string);
对于单节点应用来说,注册消息类型完全是可选的。但是对于remote actor(用于分布式应用的actor)来说,必须在每个actor定义的地方注册消息的类型,可以是系统默认支持的消息类型,如string,也可以是自定义的消息类型,如某种类型的数据结构。
对于Remote actor和Message Registration的理解,可以通过我的另外一篇译文做更深入的了解。
原文地址: