I am starting with designing thread safe data structure and looking to implement a concurrent vector that hides multi-threading complexity for the user and that offers thread safe clear function (which is not offered by Intel TBB::ConcurrentVector) . Considering a concurrent vector which is based on std::vector and locking a mutex for each call of clear or push_back functions of std::vector, how to iterate thread safely through the concurrent vector using for loop? in other terms, how to make iterate_thread in this minimal reproducible example thread safe?
#include <bits/stdc++.h>
using namespace std; // for the sake of minimal reproducible example
template<class T>
class ConcurrentVector {
public:
ConcurrentVector() : mMutex{}
, mVector{}
{}
auto begin(){
scoped_lock lk(mMutex);
return mVector.begin();
}
auto end(){
scoped_lock lk(mMutex);
return mVector.end();
}
T& push_back(T t) {
scoped_lock lk(mMutex);
mVector.push_back(move(t));
return mVector.back();
}
void clear() {
scoped_lock lk(mMutex);
mVector.clear();
}
private:
mutex mMutex;
vector<T> mVector;
};
int main(){
ConcurrentVector<int> vec{};
atomic_bool terminate_flag = false;
jthread fill_thread{[&](){
static int i = 0;
while(!terminate_flag){
this_thread::sleep_for(10ms);
vec.push_back(++i);
}
}};
jthread clear_thread{[&](){
while(!terminate_flag){
this_thread::sleep_for(1s);
vec.clear();
}
}};
jthread iterate_thread{[&](){
while(!terminate_flag){
this_thread::sleep_for(400ms);
for(auto i : vec){
cout << i << ", ";
}
cout << '\n';
}
}};
this_thread::sleep_for(3s);
terminate_flag = true;
return 0;
}
Calling clear function in iterate_thread will invalidate iterator and actually, I am thinking of these two options:
- Add getter function in ConcurrentVector to return a copy of the std::vector and iterating through it, but the issue here is that if clear is called in between, I will be iterating through the copy not the real existing data in the vector
- or add a for_each function in ConcurrentVector that takes a callable, but the issue here is lack of safety as the user may be calling a locking function which causes a deadlock with the vector mutex
Are there other alternatives or could any of the two mentioned improved?
for_eachwith a callable). You can use a templated method to support all kinds of callables (lambda, std::function etc.).data()and then modify as they like, that a mutex was locked during that call does not do anything for protecting the elements#include <bits/stdc++.h>+using namespace std;== you just committed C++ suicide;