一个详细的例子

  我们再来看一个详细的例子,借此了解 Theron 带来的便利。生产者消费者的问题是一个经典的线程同步问题,我们来看看 Theron 如何解决这个问题:

#include <stdio.h>

#include <Theron/Framework.h>
#include <Theron/Actor.h>

const int PRODUCE_NUM = 5;

class Producer : public Theron::Actor
{
public:
    inline Producer(): m_item(0)
    {
        RegisterHandler(this, &Producer::Produce);
    }

private:
    // 生产者生产物品
    inline void Produce(const int& /* message */, const Theron::Address from)
    {
        int count(PRODUCE_NUM);

        while (count--)
        {
            // 模拟一个生产的时间
#ifdef _MSC_VER
            Sleep(1000);
#else
            sleep(1);
#endif

            printf("Produce item %d ", m_item);
            if (!Send(m_item, from))
                printf("Failed to send message! ");
            ++m_item;
        }
    }

    // 当前生产的物品编号
    int m_item;
};

class Consumer : public Theron::Actor
{
public:
    inline Consumer(): m_consumeNum(PRODUCE_NUM)
    {
        RegisterHandler(this, &Consumer::Consume);
    }

private:
    inline void Consume(const int& item, const Theron::Address from)
    {
        // 模拟一个消费的时间
#ifdef _MSC_VER
        Sleep(2000);
#else
        sleep(2);
#endif

        printf("Consume item %d ", item);
        --m_consumeNum;

        // 没有物品可以消费请求生产者进行生产
        if (m_consumeNum == 0)
        {
            if (!Send(0, from))
                printf("Failed to send message! ");
            m_consumeNum = PRODUCE_NUM;
        }
    }

    int m_consumeNum;
};

int main()
{
    Theron::Framework framework;

    Theron::ActorRef producer(framework.CreateActor<Producer>());
    Theron::ActorRef consumer(framework.CreateActor<Consumer>());

    if (!framework.Send(0, consumer.GetAddress(), producer.GetAddress()))
        printf("Failed to send message! ");

    // 这里使用 Sleep 来避免主线程结束
    // 这样做只是为了简单而并不特别合理
    // 在实际的编写中,我们应该使用 Receiver
#ifdef _MSC_VER
    Sleep(100000);
#else
    sleep(100);
#endif

    return 0;
}

  生产者生产物品,消费者消费物品,它们并行进行,我们没有编写创建线程的代码,没有构建共享内存,也没有处理线程的同步。这一切都很轻松的完成了。

  代价和设计

  和传统的多线程程序相比 Theron 有不少优势,通过使用 Actor,程序能够自动的并行执行,而无需开发者费心。Actor 总是利用消息进行通信,消息必须拷贝,这也意味着我们必须注意到,在利用 Actor 进行并行运算的同时需避免大量消息拷贝带来的额外开销。

  Actor 模型强调了一切皆为 Actor,这自然可以作为我们使用 Theron 的一个准则。但过多的 Actor 存在必然导致 Actor 间频繁的通信。适当的使用 Actor 并且结合 Object 模型也许会是一个不错的选择,例如,我们可以对系统进行适当划分,得到一些功能相对独立的模块,每个模块为一个 Actor,模块内部依然使用 Object 模型,模块间通过 Actor 的消息机制进行通信。

  Theron 的未来

  Theron 是个有趣的东西,也许你没有使用过它,你也不了解 Actor 模型,但是 Actor 的思想却不新鲜,甚至你可能正在使用。目前来说,我还没有找到 Theron 在哪个实际的商业项目中使用,因此对 Theron 的使用还存在一些未知的因素。还有一些特性,诸如跨主机的分布式的并行执行是 Theron 不支持的,这些都限制了 Theron 的使用,不过作者正在积极的改变一些东西(例如,作者表示会在今后添加 Remote Actors)。无论 Theron 未来如何,Theron 以及 Actor 模型带来的思想会让我们更加从容面对多核的挑战。