2011-05-04 59 views
4

我想構建一個需要由一個線程執行的函數的工作隊列,並且可以由很多線程提供。爲了做到這一點,我打算使用boost :: packaged_task和boost :: unique_future。這個想法是你會這樣做的:如何使用返回任意類型的函數來創建一個持有boost :: packaged_task <>的隊列?

Foo value = queue.add(myFunc).get();

這會阻塞,直到函數被執行。所以queue.add(...)接受一個boost :: function,並返回一個boost :: unique_future。然後在內部使用boost :: function爲其構造函數創建boost :: packaged_task。

我遇到的問題是boost :: function < ...>每次都不一樣。具體來說,它的返回值將會改變(但函數永遠不會採用任何參數)。因此,我必須有一個附加功能,看起來像:

template <typename ResultType> 
boost::unique_future<ResultType> add(boost::function<ResultType()> f) { 
    boost::packaged_task<boost::function<ResultType()> > task(f); 
    queue.push_back(task); 
    return task.get_future(); 
} 

好吧,這似乎並沒有太糟糕了,但後來我跑進如何定義「排隊」的問題。我想我沒有選擇,只能使用boost ::任何,因爲該類型將不是恆定:

std::list<boost::any> queue; // note: I'm not concerned with thread-safety yet 

但後來我遇到一個問題,當我試圖實現我的executeSingle(只需關閉單個項目要執行的隊列):

void executeSingle() { 
    boost::any value = queue.back(); 
    boost::packaged_task<?> task = boost::packaged_task<?>(boost::move(value)); 
    // actually execute task 
    task(); 
    queue.pop_back(); 
} 

'?'表示我不確定的事情。我不能用模板調用executeSingle,因爲它是從一個單獨的線程調用的。我試着使用boost ::任何,但我得到的錯誤:

conversion from 'boost::any' to non-scalar type boost::detail::thread_move_t<boost:thread>' requested. 

有趣的是,我其實並不關心在這一點packaged_task的返回類型,我只是想執行它,但我可以弄清楚模板的細節。

任何有識之士將不勝感激!

+0

這個錯誤源於這樣一個事實,即當你真的應該使用'boost :: any_cast'時,你試圖從'any'中構造一個'packaged_task'。但請參閱下面的答案。 – 2011-05-05 04:15:50

回答

5

您應該存儲boost::function<void()>'s。請注意,boost::packaged_task<R>::operator()不會返回任何內容;它會填充關聯的boost::future。事實上,即使它返回了一些東西,您仍然可以使用boost::function<void()>,因爲您仍然對返回值沒有興趣:所有您關心的是致電queue.back()()。如果是這種情況,boost::function<void()>::operator()將負責爲您丟棄返回的值。

作爲一個小紙條,你可能想改變一個泛型類型Functor而非boost::function將模板您add方法的簽名,並使用boost::result_of得到結果類型boost::packaged_task

我的整體建議:

template<typename Functor> 
boost::future<typename boost::result_of<Functor()>::type> 
queue::add(Functor functor) // assuming your class is named queue 
{ 
    typedef typename boost::result_of<Functor()>::type result_type; 
    boost::packaged_task<result_type> task(functor); 
    boost::unique_future<result_type> future = task.get_future(); 
    internal_queue.push_back(boost::move(task)); // assuming internal_queue member 
    return boost::move(future); 
} 

void 
queue::executeSingle() 
{ 
    // Note: do you really want LIFO here? 
    queue.back()(); 
    queue.pop_back(); 
} 

編輯

如何照顧布展語義裏面queue::add

typedef typename boost::result_of<Functor()>::type result_type; 
typedef boost::packaged_task<result_type> task_type; 
boost::shared_ptr<task_type> task = boost::make_shared<task_type>(functor); 
boost::unique_future<result_type> future = task->get_future(); 

/* boost::shared_ptr is possibly move-enabled so you can try moving it */ 
internal_queue.push_back(boost::bind(dereference_functor(), task)); 

return boost::move(future); 

其中dereference_functor可能是:

struct dereference_functor { 
    template<typename Pointer> 
    void 
    operator()(Pointer const& p) const 
    { 
     (*p)(); 
    } 
}; 

你也可以替換bind表達的更加清晰

boost::bind(&task_type::operator(), task) 

這也並不需要自定義函數對象。但是,如果task_type::operator()有多個重載,則可能需要消歧;如果Boost.Thread的未來變化引入過載,代碼也可能會中斷。

+0

雖然我還沒有得到它的工作,但看起來不錯!我注意到的一件事是,我認爲第二行應該在'>'之後有':: type'。我碰到兩個問題:(1)我似乎沒有未來,只有unique_future,(2)push_back和return都抱怨unique_future的構造函數是私有的。對於push_back,我添加了一個boost :: move(task),它解決了這個問題,但這似乎不適用於返回。 – 2011-05-06 22:41:26

+0

@Abe Schneider感謝您指出錯誤 - 「future」是「std」的一部分,而不是「boost」! '私人'構造函數確實意味着這些類不可複製,而移動它們是正確的。不過,我不知道回報如何。作爲解決方法,您可以將'unique_future'更改爲'shared_future'。 – 2011-05-06 23:09:43

+0

顯然我輸入得太快了。我錯了,錯誤實際上是因爲:「internal_queue.push_front(boost :: move(task));」與錯誤「/usr/local/include/boost/function/function_template.hpp:153:error:no match to call to'(boost :: detail :: thread_move_t >)()'」。難道是因爲我的內部隊列爲:「std :: list > internal_queue」? – 2011-05-07 02:48:46

1

您使用老式的虛擬功能。用virtualexecute方法定義一個基類task_base,然後定義一個模板派生類,該派生類包含特定的任務實例。沿線的東西:

struct task_base { 
    virtual void execute() = 0; 
}; 
template<typename ResultType> 
struct task_holder : task_base { 
    task_holder(boost::packaged_task<boost::function<ResultType()> >&& task) 
    : m_task(task) { } 
    void execute() { 
    m_task(); 
    } 
private: 
    boost::packaged_task<boost::function<ResultType()> > m_task; 
}; 

並定義您的隊列以容納unique_ptr<task_base>。這實質上就是boost::any所做的,只有你會使用特定功能,即execute

注意:未經測試的代碼!而且我還不是很熟悉右值引用。這只是爲了讓你瞭解代碼的外觀。

+0

嗯。我嘗試了這種方法,但由於unique_future的構造函數存在私有問題,無法使其工作。如果我理解正確,應該使用boost :: move,但它不適用於返回值。另外,不幸的是,我目前的編譯器(Apple的默認)不支持rval引用。這可能是時候切換編譯器了...... – 2011-05-06 22:38:22

0

有點遲來,但您可能要考慮使用Boost.Asio而不是滾動您自己的隊列運行器解決方案。

雖然這是一個I/O庫,但它支持異步調用,就像這樣。只需在某個地方定義一個io_service,在一個線程內運行它,然後在該線程上調用post仿函數。

相關問題