I have a class managing vector data using the singleton pattern.
class DataManager
{
public:
static DataManager& GetInstance() {
static DataManager instance;
return instance;
}
const vector<int>& GetData() const{return data;}
void SetData(vector<int>& data_)
{
data.swap(data_);
}
private:
vector<int> data;
};
I need to access this data and modify it within a thread. However, if the data is released while accessing it, the program will throw an error.
I made the following attempts:
Pass the lock as a parameter:
class DataManager
{
prublic:
const std::vector<int>& GetRects(std::unique_lock<std::mutex>& lock) const
{
lock = std::unique_lock<std::mutex>(m_mutex_data);
return data;
}
...
private:
mutable std::mutex m_mutex_data;
}
This can work, but it's strange. Is there a better way?
Returning a lock and relying on the user of the class to unlock is very shaky.
You mentioned that you need to modify the data in a thread-safe way.
For this you can add a public method to DataManager
that will accept a callable that will modify the data, and invoke it under the lock.
Something like (just a sketch):
class DataManager
{
public:
template <typename T>
void TransformDataUnderLock(T && transformer)
{
std::lock_guard<std::mutex> lock(m_mutex_data);
transformer(data);
}
// ...
private:
std::vector<int> data;
mutable std::mutex m_mutex_data;
};
Notes:
You should also apply similar scope locking in your SetData
method (or any other method that accesses data
).
This patern might not protect against a mallicious user, but it will protect against accidental wrong usage of the accessed data and the lock (which can be much more common).
This pattern might cause a deadlock due to the fact that the lock is held during invocation of the transformer
code which you have no control over. But as long as it is a simple transformation of the data
you should be fine.