C ++设计帮助模板化虚函数

时间:2012-01-10 17:48:30

标签: c++ boost-thread

我正在尝试基于一个使用boost.threadpool抽象线程池的公共类来实现许多类。我有一些有用的东西(在osx 10.7.2上的Xcode中)但我真的不确定它的好设计还是它的安全性(很大程度上是因为我在线阅读有关使用虚拟成员函数的内容)与模板)。我正在寻找一些关于实现这样的事情的最佳方式的风格建议。我在这里学习,所以我知道很多这将是'糟糕的形式'......

我有一个名为'workqueue'的基类,如下所示:

template <typename T> 
class Workqueue{

    private:
        pool            *pThreadPool;

    public:
        Workqueue       (int);        
        void Start      (T);        
        void Schedule   (T);
        virtual bool Process(T) {return true;}
};

template <typename T> Workqueue<T>::Workqueue(int thread_count){
    pThreadPool = new pool(thread_count);
}

template <typename T> void Workqueue<T>::Start(T data){
    pThreadPool->schedule(boost::bind(&Workqueue::Process,this, data));  
    pThreadPool->wait();
}

template <typename T> void Workqueue<T>::Schedule(T data){
    pThreadPool->schedule(boost::bind(&Workqueue::Process,this, data));    
}

然后我根据这个类定义一个新服务:

struct Service1Data{
    string item_data;
};

class MyService : public Workqueue<Service1Data> {    
public:
    MyService       (int);
    bool Process    (Service1Data);        
};

MyService::MyService(int workers) : Workqueue<Service1Data>(workers) {}

bool MyService::Process(Service1Data service_data){
    cout << "in process (" << service_data.item_data << ")" << endl;     
    return true;
}

(我删除了尽可能多的代码以保持简单,因此显示将永远运行,因为它不断提交新工作)。我使用这样的服务:

    MyService *service1 = new MyService(5);

    Service1Data x;
    x.item_data = "testing";
    service1->Start(x);

    // will wait until no more work.
    delete service1;

所以我的具体问题:

首先(请温柔......)是这种糟糕的形式,有更好的方法吗? (为什么?)

其次 - 考虑到虚拟/模板问题,这是否安全?我在某处读到,如果课程本身是模板化的,那么它应该是安全的,我认为我理解基本的vtable问题 - 但实际上并不确定具体细节。

第三 - 基础工作队列需要在'h'文件中包含要定义的类定义的成员定义。不知道为什么会这样 - 我想这是一个关于虚拟/模板问题的链接器问题,所以让我很紧张。

所有人都非常感激地收到了..

克里斯

3 个答案:

答案 0 :(得分:1)

我认为,您不应将处理数据处理队列混合在一起。

我的意思是,您Process中不应该使用Workqueue方法。 Data可以自行处理,或者您的队列可以将处理函数作为( template ?)参数。

然后你摆脱虚拟功能的所有问题。 YourService类应该聚集Workqueue并且可以提供过程功能。

另外,我怀疑你是否真的需要Workqueue。您可以在pThreadPool中使用YourService

如果您需要一个通用的服务接口,您应该明确指定它。分别。你的继承链看起来不清楚。继承意味着。为什么YourService Workqueue我不相信!我认为YourService可以使用任何类型的队列。但用法是聚合。

编辑: 代码如下所示:

template<typename Processor>
class WorkQueue
{
public:
    WorkQueue(int count, Processor& processor):_processor(processor),_pool(count) {}

    template <typename Data>
    void schedule(const Data& data)
    {
        _pool->schedule(std::bind(&Processor::process,_processor, data));
    }

    template <typename Data>
    void run(const Data& data)
    {
        schedule(data);
        _pool->wait();
    }

private:
    Processor& _processor;
    pool _pool;
};

class Service
{
public:
    virtual void run() = 0;
    virtual ~Service() {}
};

struct ServiceParams
{
    int param;
};

class MyService: public Service
{

    friend class WorkQueue<MyService>;
public:
    MyService(const ServiceParams& params): _params(params), _queue(1, *this) {}
    void run() { return _queue.run(_params); }
private:
    ServiceParams _params;
    WorkQueue<MyService> _queue;

    void process(const ServiceParams& params) {std::cout <<"hello, world\n";}
};

编辑:我最初认为用法为:

ServiceData data;
Service* service = new MyService(data);
service->run();
delete service;

答案 1 :(得分:0)

我可以明确指出的小事情:

  • 在创建自动对象时过度使用new和delete。

例如,如果您的工作队列和池具有相同的生命周期,那么:

template <typename T>  class Workqueue
{      
    private:
      pool            threadPool;  
 // // etc
};

template< typename T >
Workqueue::Workqueue( int numThreads ) : threadPool( numThreads )
{
}

你的基类需要一个虚拟析构函数,因为它可以调用Schedule而不是实现相同的代码行(使用boost :: bind)两次。理想情况下,只接受int成员的构造函数将被声明为显式。

您可能需要逻辑来等待线程完成。

答案 2 :(得分:0)

我认为好的设计应该单独隔离排队和工作/任务。在您的设计中,两者都是紧密耦合的。如果您想为每种类型的工作/任务创建单独的池,这种设计是很好的。

另一种方法是创建一个包含Work函数的单独process类。然后,您的MyService会延长Work。并且WorkQueue类将接受Work,这意味着任何派生类。这种方法本质上更通用。因此,相同的工作队列可以接受不同类型的工作/任务。下面的代码插图将清楚更多。

如果您想为不同类型的数据使用不同的池,也可以使用添加此方法。它本质上更灵活。

template <typename T> 
class Work{
    T data; // contains the actual data to work on
    public:
        Work(T data) : data(data) {} // constructor to init data
        virtual bool Process(T) {return false;} // returns false to tell process failed
        T getData() { return data; } // get the data
};

class MyWork : public Work<Service1Data> {    
public:
    MyService (Service1Data data) : 
      Work(data) {}
    bool Process (Service1Data); // Implement your work specific process func
};

bool MyWork::Process(Service1Data service_data){
    cout << "in process (" << service_data.item_data << ")" << endl;     
    return true;
}

class Workqueue{

    private:
        pool            *pThreadPool;

    public:
        Workqueue       (int);        
        void Start      (Work);        
        void Schedule   (Work);
};

Workqueue::Workqueue(int thread_count){
    pThreadPool = new pool(thread_count);
}

void Workqueue::Start(Work workToDo){
    pThreadPool->schedule(boost::bind(&Work::Process,this, workToDo.getData()));  
    pThreadPool->wait();
}

void Workqueue::Schedule(Work data){
    pThreadPool->schedule(boost::bind(&Work::Process,this, workToDo.getData()));    
}

<强>用法

Service1Data x;
x.item_data = "testing";
MyWork myWork(x);


Workqueue wq = new Workqueue(5);
wq->Start(myWork);

// will wait until no more work.
delete service1;

现在要为不同类型的工作/任务创建不同的池,创建两个具有不同池大小的Workqueue,然后只提供一种类型的工作和其他类型的工作。

注意:上面的代码可能包含语法错误,它只是传达设计。将其视为伪代码。