[英]Multithreaded read-many, write-seldom array/vector iteration in C++
我需要以只讀方式幾乎不斷迭代一系列結構,但是對於每1M +讀取,其中一個線程可能附加一個項目。 我認為使用互斥鎖在這里會有點過分,我也會在某處讀到r / w鎖具有讀者自身的缺點。
我正在考慮在std :: vector上使用reserve(),但是這個答案使用索引安全方式迭代STL容器以避免使用鎖? 似乎無效。
什么方式的任何想法可能是最快的? 最重要的是讓讀者能夠以盡可能少的爭用快速有效地進行迭代。 寫入操作不是時間敏感的。
更新:我的另一個用例是“列表”可能包含指針而不是結構。 即,std :: vector。 相同的要求適用。
更新2:假設的例子
全球可訪問:
typedef std::vector<MyClass*> Vector;
Vector v;
v.reserve(50);
讀者線程1-10 :(這些運行一直運行)
.
.
int total = 0;
for (Vector::const_iterator it = v.begin(); it != v.end(); ++it)
{
MyClass* ptr = *it;
total += ptr->getTotal();
}
// do something with total
.
.
作家帖子11-15:
MyClass* ptr = new MyClass();
v.push_back(ptr);
這基本上就是這里發生的事情。 線程1-15可以同時運行,盡管通常只有1-2個讀取線程和1-2個寫入器線程。
我認為可以在這里工作的是vector
自己實現,如下所示:
template <typename T> class Vector
{
// constructor will be needed of course
public:
std::shared_ptr<const std::vector<T> > getVector()
{ return mVector; }
void push_back(const T&);
private:
std::shared_ptr<std::vector<T> > mVector;
};
然后,每當讀者需要訪問特定的Vector
,他們應該調用getVector()
並保持返回的shared_ptr
直到讀完為止。
但是作者應該總是使用Vector
的push_back
來增加新的價值。 然后,此push_back
應檢查mVector.size() == mVector.capacity()
,如果為true,則分配新 vector
並將其分配給mVector
。 就像是:
template <typename T> Vector<T>::push_back(const T& t)
{
if (mVector->size() == mVector->capacity())
{
// make certain here that new_size > old_size
std::vector<T> vec = new std::vector<T> (mVector->size() * SIZE_MULTIPLIER);
std::copy(mVector->begin(), mVector->end(), vec->begin());
mVector.reset(vec);
}
// put 't' into 'mVector'. 'mVector' is guaranteed not to reallocate now.
}
這里的想法受到RCU(讀取 - 復制 - 更新)算法的啟發。 如果存儲空間耗盡,只要至少有一個讀取器訪問舊存儲,新存儲不應使舊存儲無效。 但是,應該分配新存儲,並且任何讀者在分配之后都應該能夠看到它。 一旦沒有人再使用舊存儲應該被解除分配(所有讀者都已完成)。
由於大多數硬件架構提供了一些原子遞增和遞減的方法,我認為shared_ptr
(以及Vector
)將能夠完全無鎖地運行。
但是,這種方法的一個缺點是,根據讀者持有shared_ptr
時間長短,您最終可能會獲得data
多個副本。
PS:希望我在代碼中沒有犯過太多令人尷尬的錯誤:-)
...在std :: vector上使用reserve()...
只有在可以保證向量永遠不需要增長的情況下,這才有用。 你已經說明了如果物品沒有超出限制的數字,那么你就無法保證。
盡管存在鏈接問題,但您可以設想使用std::vector
來為您管理內存,但是需要額外的邏輯層來解決已接受答案中確定的問題。
實際答案是:最快的做法是最小化同步量。 最小同步量取決於您未指定的代碼和用法的詳細信息。
例如,我使用固定大小的塊的鏈表列出了一個解決方案。 這意味着您的常見用例應該與數組遍歷一樣高效,但是您可以在不重新分配的情況下動態增長。
但是,實施結果對以下問題很敏感:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.