我正在尝试基于一个使用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'文件中包含要定义的类定义的成员定义。不知道为什么会这样 - 我想这是一个关于虚拟/模板问题的链接器问题,所以让我很紧张。
所有人都非常感激地收到了..
克里斯
答案 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)
我可以明确指出的小事情:
例如,如果您的工作队列和池具有相同的生命周期,那么:
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
,然后只提供一种类型的工作和其他类型的工作。
注意:上面的代码可能包含语法错误,它只是传达设计。将其视为伪代码。