I am trying to make my_class thread-safe like so.
class my_class
{
const std::vector<double>&
get_data() const
{ //lock so that cannot get_data() while setting data
lock l(m_mutex);
return m_data;
}
void
run()
{
vector<double> tmp;
//some calculations on tmp.
{ //lock so that cannot get_data() while setting m_data
lock l(m_mutex);
m_data = tmp; //set the data
}
}
private:
std::vector<double> m_data;
mutex m_mutex;
my_class(); //non-copyable
}
run() and get_data() may be called by different openmp threads and so I introduce a lock.
(Since am using openmp, m_mutex and lock are RAII wrappers around omp_init_lock(); etc. commands).
However, the lock on get_data () is expensive to create and destroy (The most expensive operation when I profile my code - I call get_data() a lot).
Is is possible to reorganise my_class to remove the lock in get_data()? Or is this lock the unavoidable cost of parallelising the code?
First step would be to look into read-write locks: this way multiple readers will not block each other.
The next step would be using lock-free or wait-free operations. There are plenty of resources online describing them better than I would be able to. Just one note: lock-free approaches deal with atomic (interlocked) operations, which means the data size needs to be small. If you go this route, you'll be atomically replacing a pointer to your vector, not the whole vector. This means your class will get a bit more complex and will deal with some pointers and memory management.
It may be cheaper to use a critical section around get_data/run functions, you will not incur additional setup/teardown overhead (as the critical section is statically initialized), but this would also synchronize other instances of the class.
Related
Suppose I have a class:
class WonnaBeMovedClass
{
public:
WonnaBeMovedClass(WonnaBeMovedClass&& other);
void start();
private:
void updateSharedData();
std::vector<int> _sharedData;
std::thread _threadUpdate;
std::mutex _mutexShaderData;
//other stuff
};
WonnaBeMovedClass::WonnaBeMovedClass(WonnaBeMovedClass&& other)
{
_sharedData = std::move(other._sharedData);
_threadUpdate = std::move(other._threadUpdate);
_mutexShaderData = std::move(other._mutexShaderData); //won't compile.
}
void WonnaBeMovedClass::start()
{
_threadUpdate = std::thread(&updateSharedData, this);
}
void WonnaBeMovedClass::updateSharedData()
{
std::lock_guard<std::mutex> lockSharedData(_mutexShaderData);
for (auto& value : _sharedData)
{
++value;
}
}
It won't compile because mutex cannot be moved. It doesn't make sense.
Then I thought that it is possible to workaround this by using pointers instead of actual variables and came up with the following:
class WonnaBeMovedClass
{
public:
WonnaBeMovedClass(WonnaBeMovedClass&& other);
void start();
private:
void updateSharedData();
std::vector<int> _sharedData;
std::unique_ptr<std::thread> _threadUpdate //pointer;
std::unique_ptr<std::mutex> _mutexShaderData //pointer;
//other stuff
};
WonnaBeMovedClass::WonnaBeMovedClass(WonnaBeMovedClass&& other)
{
_sharedData = std::move(other._sharedData);
_threadUpdate = std::move(other._threadUpdate);
_mutexShaderData = std::move(other._mutexShaderData); //won't compile.
}
void WonnaBeMovedClass::start()
{
_threadUpdate = std::make_unique<std::thread>(&updateSharedData, this);
}
void WonnaBeMovedClass::updateSharedData()
{
std::lock_guard<std::mutex> lockSharedData(*_mutexShaderData);
for (auto& value : _sharedData)
{
++value;
}
}
So now when I:
WonnaBeMovedClass object1;
WonnaBeMovedClass object2;
//do stuff
object1 = std::move(object2);
I actually move addresses of both mutex and thread.
It makes more sense now... Or not?
The thread is still working with the data of object1, not object2, so it still doesn't make any sense.
I may have moved the mutex, but the thread is unaware of object2. Or is it?
I am unable to find the answer so I am asking you for help.
Am I doing something completely wrong and copying/moving threads and mutexes is just a bad design and I should rethink the architecture of the program?
Edit:
There was a question about actual purpose of the class. It is actually a TCP/IP client (represented as a class) that holds:
latest data from the server (several data tables, similar to std::vector).
contains methods that manage threads (update state, send/receive messages).
More that one connection could be established at a time, so somewhere in a code there is a std::vector<Client> field that represents all active connections.
Connections are determined by the configuration file.
//read configurations
...
//init clients
for (auto& configuration : _configurations)
{
Client client(configuration);
_activeClients.push_back(client); // this is where compiler reminded me that I am unable to move my object (aka WonnaBeMovedClass object).
}}
I've changed _activeClients from std::vector<Client> to std::vector<std::unique_ptr<Client>> and modified initialization code to create pointer objects instead of objects directly and worked around my issues, but the question remained so I decided to post it here.
Let's break the issue in two.
Moving mutexes. This cannot be done because mutexes are normally implemented in terms of OS objects which must have fixed addresses. In other words, the OS (or the runtime library, which is the same as the OS for our purposes) keeps an address of your mutex. This can be worked around by storing (smart) pointers to mutexes in your code, and moving those instead. The mutex itself doesn't move. Thread objects can be moved so there's no issue.
Moving your own data whereas some active code (a thread or a running function or a std::function stored somewhere or whatever) has the address of your data and can access it. This is actually very similar to the previous case, only instead of the OS it's your own code that holds on the data. The solution, as before, is in not moving your data. Store and move a (smart) pointer to the data instead.
To summarise,
class WonnaBeMovedClass
{
public:
WonnaBeMovedClass
(WonnaBeMovedClass&& other);
void start();
private:
struct tdata {
std::vector<int> _sharedData;
std::thread _threadUpdate;
std::mutex _mutexShaderData;
};
std::shared_ptr<tdata> data;
static void updateSharedData(std::shared_ptr<tdata>);
};
void WonnaBeMovedClass::start()
{
_threadUpdate = std::thread(&updateSharedData, data);
}
It makes more sense now... Or not?
Not really.
If an std::mutex gets moved, the other threads will not be aware of the modification of memory address of that mutex! This discards thread safety.
However, a solution with std::unique_ptr exists in Copy or Move Constructor for a class with a member std::mutex (or other non-copyable object)?
Last, but not least C++14 seems to have something to bring into the play. Read more in How should I deal with mutexes in movable types in C++?
WonnaBeMovedClass is a handle holding thread and mutex, so it is not a bad design to provide them with a move semantics (but not copy).
The second solutions looks fine, but don't forget about proper resource management for your mutex (construct and destruct). I don't really undertand the real life purpose of the class, so depending on the whole solution desing, it might be better to use shared_ptr instead of unique_ptr (in case that multiple WonnaBeMovedClass can share same mutex).
std::thread is itself a handle to system thread, so it doesn't have to be wrapped in a pointer, resource management (i.e OS thread handle) is managed by the standard library itself.
Note that mutex are actually kernel objects (usually implemented as an opaque pointer, for example in Windows API), and thus should not be modified or changed by user code in any way.
I have a object and all its function should be executed in sequential order.
I know it is possible to do that with a mutex like
#include <mutex>
class myClass {
private:
std::mutex mtx;
public:
void exampleMethod();
};
void myClass::exampleMethod() {
std::unique_lock<std::mutex> lck (mtx); // lock mutex until end of scope
// do some stuff
}
but with this technique a deadlock occurs after calling an other mutex locked method within exampleMethod.
so i'm searching for a better resolution.
the default std::atomic access is sequentially consistent, so its not possible to read an write to this object at the same time, but now when i access my object and call a method, is the whole function call also atomic or more something like
object* obj = atomicObj.load(); // read atomic
obj.doSomething(); // call method from non atomic object;
if yes is there a better way than locking the most functions with a mutex ?
Stop and think about when you actually need to lock a mutex. If you have some helper function that is called within many other functions, it probably shouldn't try to lock the mutex, because the caller already will have.
If in some contexts it is not called by another member function, and so does need to take a lock, provide a wrapper function that actually does that. It is not uncommon to have 2 versions of member functions, a public foo() and a private fooNoLock(), where:
public:
void foo() {
std::lock_guard<std::mutex> l(mtx);
fooNoLock();
}
private:
void fooNoLock() {
// do stuff that operates on some shared resource...
}
In my experience, recursive mutexes are a code smell that indicate the author hasn't really got their head around the way the functions are used - not always wrong, but when I see one I get suspicious.
As for atomic operations, they can really only be applied for small arithmetic operations, say incrementing an integer, or swapping 2 pointers. These operations are not automatically atomic, but when you use atomic operations, these are the sorts of things they can be used for. You certainly can't have any reasonable expectations about 2 separate operations on a single atomic object. Anything could happen in between the operations.
You could use a std::recursive_mutex instead. This will allow a thread that already owns to mutex to reacquire it without blocking. However, if another thread tries to acquire the lock it will block.
As #BoBTFish properly indicated, it is better to separate your class's public interface, which member functions acquire non-recursive lock and then call private methods which don't. Your code must then assume a lock is always held when a private method is run.
To be safe on this, you may add a reference to std::unique_lock<std::mutex> to each of the method that requires the lock to be held.
Thus, even if you happen to call one private method from another, you would need to make sure a mutex is locked before execution:
class myClass
{
std::mutex mtx;
//
void i_exampleMethod(std::unique_lock<std::mutex> &)
{
// execute method
}
public:
void exampleMethod()
{
std::unique_lock<std::mutex> lock(mtx);
i_exampleMethod(lock);
}
};
For example consider
class ProcessList {
private
std::vector<std::shared_ptr<SomeObject>> list;
Mutex mutex;
public:
void Add(std::shared_ptr<SomeObject> o) {
Locker locker(&mutex); // Start of critical section. Locker release so will the mutex - In house stuff
list.push_back(std::make_shared<SomeObject>(o).
}
void Remove(std::shared_ptr<SomeObject> o) {
Locker locker(&mutex); // Start of critical section. Locker release so will the mutex - In house stuff
// Code to remove said object but indirectly modifying the reference count in copy below
}
void Process() {
std::vector<std::shared_ptr<SomeObject>> copy;
{
Locker locker(&mutes);
copy = std::vector<std::shared_ptr<SomeObject>>(
list.begin(), list.end()
)
}
for (auto it = copy.begin(), it != copy.end(); ++it) {
it->Procss(); // This may take time/add/remove to the list
}
}
};
One thread runs Process. Multiple threads run add/remove.
Will the reference count be safe and always correct - or should a mutex be placed around that?
Yes, the standard (at ยง20.8.2.2, at least as of N3997) that's intended to require that the reference counting be thread-safe.
For your simple cases like Add:
void Add(std::shared_ptr<SomeObject> o) {
Locker locker(&mutex);
list.push_back(std::make_shared<SomeObject>(o).
}
...the guarantees in the standard are strong enough that you shouldn't need the mutex, so you can just have:
void Add(std::shared_ptr<SomeObject> o) {
list.push_back(std::make_shared<SomeObject>(o).
}
For some of your operations, it's not at all clear that thread-safe reference counting necessarily obviates your mutex though. For example, inside of your Process, you have:
{
Locker locker(&mutes);
copy = std::vector<std::shared_ptr<SomeObject>>(
list.begin(), list.end()
)
}
This carries out the entire copy as an atomic operation--nothing else can modify the list during the copy. This assures that your copy gives you a snapshot of the list precisely as it was when the copy was started. If you eliminate the mutex, the reference counting will still work, but your copy might reflect changes made while the copy is being made.
In other words, the thread safety of the shared_ptr only assures that each individual increment or decrement is atomic--it doesn't assure that manipulations of the entire list are atomic as the mutex does in this case.
Since your list is actually a vector, you should be able to simplify the copying code a bit to just copy = list.
Also note that your Locker seems to be a subset of what std::lock_guard provides. It appears you could use:
std::lock_guard<std::mutex> locker(&mutes);
...in its place quite easily.
It will be an overhead to have a mutex for reference counting.
Internally, mutexes use atomic operations, basically a mutex does internal thread safe reference counting. So you can just use atomics for your reference counting directly instead of using a mutex and essentially doing double the work.
Unless your CPU architecture have an atomic increment/decrement and you used that for reference count, then, no, it's not safe; C++ makes no guarantee on the thread safety of x++/x-- operation on any of its standard types.
Use atomic<int> if your compiler supports them (C++11), otherwise you'll need to have the lock.
Further references:
https://www.threadingbuildingblocks.org/docs/help/tbb_userguide/Atomic_Operations.htm
Given the situation where a Producer Thread creates an object o of an arbitrary type O that then must be read (and only read) by a Consumer Thread, which is the ideal way to accomplish this in an efficient and thread safe way in C++11?
As of now my implementation relies on a producer-consumer model, using a mutexed/conditioned work queue based on a template:
template<typename T> class WorkQueue {
std::list<T> queue;
std::mutex mut;
std::condition_variable cond;
public:
...
}
If o's type is defined as follows:
class WorkItem {
const int value;
public:
WorkItem(int v) : value(v) {}
const int getValue() {
return value;
}
}
and the threads produce-consume WorkItem objects in the heap like this:
WorkQueue<WorkItem*> workQueue;
...
void producerThread() {
workQueue.add(new WorkItem(0));
}
void consumerThread() {
WorkItem *item = workQueue.remove();
doSomething(item.getValue());
delete item;
}
Am I guaranteed that the heap objects will be properly readable by the consumer?
If the answer happens to be no, I'm guessing that WorkItem's members should be protected by a mutex, but that would be quite an inefficient solution, as no locks should be required after the WorkItem has been made available to all the threads. Alternatively, I'm guessing that an atomization based approach could be better in this case.
What you propose looks OK. You might store smart pointers like std::unique_ptr<WorkItem> to gain exception safety if e.g. doSomething() throws. And you should be aware of lock-free queues, but you do not necessarily need to use one, especially if it would mean adding new library dependencies to your project. Finally, all that new memory allocation may someday be a bottleneck, in which case look into using an object pool or otherwise reusing your WorkItems. But most of those things would be premature optimizations right now.
I believe I've got a good handle on at least the basics of multi-threading in C++, but I've never been able to get a clear answer on locking a mutex around shared resources in the constructor or the destructor. I was under the impression that you should lock in both places, but recently coworkers have disagreed. Pretend the following class is accessed by multiple threads:
class TestClass
{
public:
TestClass(const float input) :
mMutex(),
mValueOne(1),
mValueTwo("Text")
{
//**Does the mutex need to be locked here?
mValueTwo.Set(input);
mValueOne = mValueTwo.Get();
}
~TestClass()
{
//Lock Here?
}
int GetValueOne() const
{
Lock(mMutex);
return mValueOne;
}
void SetValueOne(const int value)
{
Lock(mMutex);
mValueOne = value;
}
CustomType GetValueTwo() const
{
Lock(mMutex);
return mValueOne;
}
void SetValueTwo(const CustomType type)
{
Lock(mMutex);
mValueTwo = type;
}
private:
Mutex mMutex;
int mValueOne;
CustomType mValueTwo;
};
Of course everything should be safe through the initialization list, but what about the statements inside the constructor? In the destructor would it be beneficial to do a non-scoped lock, and never unlock (essentially just call pthread_mutex_destroy)?
Multiple threads cannot construct the same object, nor should any thread be allowed to use the object before it's fully constructed. So, in sane code, construction without locking is safe.
Destruction is a slightly harder case. But again, proper lifetime management of your object can ensure that an object is never destroyed when there's a chance that some thread(s) might still use it.
A shared pointer can help in achieving this eg. :
construct the object in a certain thread
pass shared pointers to every thread that needs access to the object (including the thread that constructed it if needed)
the object will be destroyed when all threads have released the shared pointer
But obviously, other valid approaches exist. The key is to keep proper boundaries between the three main stages of an object's lifetime : construction, usage and destruction. Never allow an overlap between any of these stages.
They don't have to be locked in the constructor, as the only way anyone external can get access to that data at that point is if you pass them around from the constructor itself (or do some undefined behaviour, like calling a virtual method).
[Edit: Removed part about destructor, since as a comment rightfully asserts, you have bigger issues if you're trying to access resources from an object which might be dead]