2017-07-29 64 views
1

考慮一個無鎖併發數據結構,其中pop()操作需要返回一個項目或false如果該cointainer是空的(而不是阻塞或投擲)。數據結構以用戶類型T爲模板,該用戶類型可能很大(但也可以是輕量級的,並且我希望在任何情況下都能提高效率)。 T必須至少可移動,但我不希望它必須是可複製的。輸出參數和移動語義

我在想這個函數的簽名應該是bool DS<T>::pop(T &item),所以這個項目被解壓爲一個out-parameter而不是返回值(用來表示成功或失敗)。但是,我如何才能將其傳遞出去?假設有一個基礎緩衝區。我會做 - 是否有意義進入參考輸出參數?缺點是用戶必須傳入一個默認構造的T,這會對有效的RAII產生一些影響,因爲如果函數失敗,結果是一個實際上未初始化其資源的對象。

另一種選擇是返回一個std::pair<bool, T>而不是使用了參數,但需要再次是默認constructible T後者持有失效的情況下,沒有資源,爲return std::make_pair(false, T)

第三種選擇是將項目返回爲std::unique_ptr<T>,但這在T是指針或其他輕量級類型的情況下會產生無用的開銷。雖然我可以在外部存儲實際項目的數據結構中存儲指針,但不僅會導致額外的解除引用和緩存未命中的損失,還會刪除直接存儲在緩衝區中的項目的自然填充,並有助於最大限度地減少生產者和消費者線程從擊中相同的緩存線。

+5

這就是'boost :: optional',很快就是'std :: optional'。 – aschepler

+0

您可以使用'std :: aligned_storage'返回可能包含或不包含對象的存儲。這基本上是'std :: optional'的意思 – KABoissonneault

回答

1
#include <boost/optional.hpp> 
#include <string> 

template<class T> 
struct atomic_queue 
{ 
    using value_type = T; 

    auto pop() -> boost::optional<T> 
    { 
     boost::optional<T> result; 

     /* 
     * insert atomic ops here, optionally filling result 
     */ 

     return result; 
    }; 

    auto push(T&& arg) -> bool 
    { 

     /* 
     * insert atomic ops here, optionally stealing arg 
     */ 

     return true; 
    }; 

    static auto make_empty_result() { 
     return boost::optional<T>(); 
    } 

}; 

struct difficult { 
    difficult(std::string); 
    difficult() = delete; 
    difficult(difficult const&) = delete; 
    difficult& operator=(difficult const&) = delete; 
    difficult(difficult &&) = default; 
    difficult& operator=(difficult &&) = default; 
}; 

extern void spin(); 

int main() 
{ 
    atomic_queue<difficult> q; 

    auto d = difficult("arg"); 
    while(not q.push(std::move(d))) 
     spin(); 

    auto popped = q.make_empty_result(); 
    while(not (popped = q.pop())) 
     spin(); 

    auto& val = popped.get(); 
}