7
\$\begingroup\$

I have implemented a thread safe vector, based on std::thread and std::vector. I am new to many of the patterns used in threading and was wondering if there's a more efficient way the locking can be implemented? Also I'm not sure what the best name is to give variables that just get stored right away (i.e. in).

Here is SafeVector.hpp

#ifndef SAFEVECTOR_HPP
#define SAFEVECTOR_HPP
#include <vector>
#include <mutex>
#include <condition_variable>
#include <string>
class SafeVector {
public:
 SafeVector() : vec(), mut(), cond() {}
 SafeVector(const SafeVector& orig) : vec(orig.vec), mut(), cond() {}
 ~SafeVector() {}
 void insert(std::string in, const int index);
 void push_back(std::string in);
 std::string& operator[](const int index);
 std::vector<std::string>::iterator begin();
 std::vector<std::string>::iterator end();
 std::vector<std::string> toVector();
private:
 std::vector<std::string> vec;
 std::mutex mut;
 std::condition_variable cond;
};
#endif /* SAFEVECTOR_HPP */

Here is SafeVector.cpp

#include <string>
#include <utility>
#include "SafeVector.hpp"
void SafeVector::insert(std::string in, const int index)
{
 std::lock_guard<std::mutex> lock(mut);
 vec[index] = std::move(in);
 cond.notify_one();
}
void SafeVector::push_back(std::string in)
{
 std::lock_guard<std::mutex> lock(mut);
 vec.push_back(std::move(in));
 cond.notify_one();
}
std::string& SafeVector::operator[](const int index)
{
 return vec[index];
}
std::vector<std::string>::iterator SafeVector::begin()
{
 return vec.begin();
}
std::vector<std::string>::iterator SafeVector::end()
{
 return vec.end();
}
std::vector<std::string> SafeVector::toVector()
{
 return vec;
}

Currently, these are the only functions I need to use on vectors but if I decide to implement more, I'm assuming the rest would be done in the same way.

asked Mar 7, 2019 at 14:17
\$\endgroup\$
1
  • \$\begingroup\$ What is the purpose of condition variable here? \$\endgroup\$ Commented Sep 1, 2020 at 4:53

1 Answer 1

3
\$\begingroup\$

We probably want to be able to move-construct, so as well as this constructor:

SafeVector(const SafeVector& orig) : vec(orig.vec), mut(), cond() {}

We probably want also:

SafeVector(SafeVector&& orig)
 : vec{std::move(orig.vec)},
 mut{},
 cond{}
{}

I'd recommend being able to construct from a standard vector:

SafeVector(std::vector vec)
 : vec{std::move(vec)},
 mut{},
 cond{}
{}

Since we always default-initialize mut and cond, we could declare their initializers in the class definition, removing the need to have them in the constructors' initializer lists.

There's no need to specify an empty destructor - just let the compiler generate it.

I think we probably want one or more assignment operators.

Big question: how can we safely use iterators we get from begin() and end()? There's no way for client code to lock the mutex until it's finished using the iterators.

answered Mar 7, 2019 at 14:44
\$\endgroup\$
0

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.