2012-01-10 53 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