I have a question regarding the term thread-safety. Let me give an example:
#include
#include
/// A thread-safe vector
class Th
One of the best and most error free ways of wrapping thread safety around a traditionally thread-unsafe class is to use a monitor:
template<class T>
class monitor
{
public:
template<typename ...Args>
monitor(Args&&... args) : m_cl(std::forward<Args>(args)...){}
struct monitor_helper
{
monitor_helper(monitor* mon) : m_mon(mon), m_ul(mon->m_lock) {}
T* operator->() { return &m_mon->m_cl;}
monitor* m_mon;
std::unique_lock<std::mutex> m_ul;
};
monitor_helper operator->() { return monitor_helper(this); }
monitor_helper ManuallyLock() { return monitor_helper(this); }
T& GetThreadUnsafeAccess() { return m_cl; }
private:
T m_cl;
std::mutex m_lock;
};
This will let you access all the methods of the wrapped class in a thread safe way:
monitor<std::vector<int>> threadSafeVector {5};
Then use
threadSafeVector->push_back(5);
or any other member function to have the call performed under a lock. See my original answer here for more information.
This will not magically make multiple calls logically threadsafe (as discuessed in other answers) but there's a way with this system to achieve this aswell:
// You can explicitly take a lock then call multiple functions
// without the overhead of a relock each time. The 'lock handle'
// destructor will unlock the lock correctly. This is necessary
// if you want a chain of logically connected operations
{
auto lockedHandle = threadSafeVector.ManuallyLock();
if(!lockedHandle->empty())
{
lockedHandle->pop_back();
lockedHandle->push_back(-3);
}
}