2012-01-10 52 views
0

我想基于一个使用boost.threadpool抽象线程池的公用类来实现一些类。我已经得到了一些可行的方法(在osx 10.7.2上的Xcode中),但我真的不确定它的好设计,或者它甚至是安全的(主要是因为我在线阅读了关于使用虚拟成员函数与模板)。我正在寻找一些风格建议,以实现像这样的最佳方式。我学习,我走在这里让我认识了很多,这将是“不好的形式” ......模板化虚拟功能的C++设计帮助

我有所谓的“工作队列”这样一个基类:

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; 

所以我的具体问题:

首先(和请温柔...)是这个坏形式,是有一个更好的方法来做到这一点? (以及为什么?)

其次 - 这是甚至安全的虚拟/模板问题?我在某个地方读到,如果课堂本身是模板化的,我认为我理解基本的问题 - 但实际上不确定具体细节。

第三 - 基准workqueue类需要在'h'文件中具有类定义的成员定义以便链接。不知道为什么会这样 - 我想这是一个与虚拟/模板问题有关的链接器问题,所以让我感到紧张。

所有帮助感激地接受..

克里斯

+0

“如果类本身被模板化了,应该是安全的” - 正确,它只是'template 虚拟空间f(T);'只是不能解决问题。如果您有模板类,则函数的参数类型已经确定,并且只需要vtable中的一个条目。 – Xeo 2012-01-10 17:52:04

+0

谢谢 - 大概是因为类模板将在编译时解析?那是对的吗? – 2012-01-10 17:58:41

+0

*任何*模板将在编译时解析 - 这是它们的本质。你只会遇到真正的虚拟函数模板的问题,因为对于函数可能调用的每个参数类型都需要不同的vtable条目,这是不可能的。 – Xeo 2012-01-10 18:00:30

回答

1

我想,你不应该混合处理数据处理队列

我的意思是,您的Workqueue中不应该有Process方法。 Data可能会自行处理,或者您的队列可以获得处理功能(模板?)参数。

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

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

如果您需要一个通用的服务接口,您应该分别指定它明确&。你的继承链看起来不清楚。继承意味着。为什么YourServiceWorkqueue我不相信!我觉得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; 
+0

听起来很有趣 - 在我的服务中不使用线程池的意义在于,我可以将它抽象出来,并使得编写多个服务变得非常简单。我的想法是从服务中删除任何队列的概念 - 这只是编写过程的一个例子(可能会提交更多特定于服务的工作,您能否告诉我如何使用聚合来构建它? – 2012-01-10 19:15:25

+0

您是否需要使用不同的参数多次启动_same_服务?(对设计很重要) – Lol4t0 2012-01-10 19:25:02

+0

感谢代码示例 - 真的有帮助,看起来不错。有人认为我有点困惑 - 使用它似乎我需要做: ServiceParams x; MyService y(x); WorkQueue z(5,y); z.run(x);如果我有这个权利(它就像那样编译),我必须用数据实例化MyService,也可以传递数据在MyService上运行,我可以从MyService的构造函数中删除数据吗? – 2012-01-11 14:10:47

0

小事情,我可以明显地指出:新

  • 过度使用和删除时,你可以自动创建对象。

例如,如果你的工作队列和游泳池有那么相同的寿命:

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

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

你的基类需要一个虚析构函数,并且因为它代表开始可以来电预约,而不是实施同一行代码(使用boost :: bind)两次。理想情况下,仅接受int成员的构造函数将被声明为显式。

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

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不同的池大小,然后给一个只有一种类型的工作和其他另一种类型的工作的。

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

+0

好吧,我想每个服务有一个单独的队列 - 然后我可以平衡资源,比如说,给serviceA一个线程和服务B的五个线程。但是 - 您的建议是有道理的,并且实际上会增加灵活性,因为我可以队列和工作之间有多对多的关系。谢谢 – 2012-01-10 19:37:26

+0

顺便说一句,你也可以通过创建两个具有不同池大小的'Workqueue'来实现这一点,然后只给予一种类型的工作和其他类型的工作。还有其他的方式来实现它,但上面会让你开始。 – havexz 2012-01-10 19:43:31