Supposed I have a class that represent some data structure called foo:
class foo{
public:
foo(){
attr01 = 0;
}
void f(){
attr01 += 5;
}
private:
int attr01;
};
class fooSingleThreadUserClass{
void usefoo(){
fooAttr.f();
}
foo fooAttr;
}
Now supposed later in software construction, I found out that I need multithreading. Should I add the mutex in foo?
class foo{
public:
foo(){
attr01 = 0;
}
void f(){
attr01Mutex.lock();
attr01 += 5;
attr01Mutex.unlock();
}
private:
int attr01;
std::mutex attr01Mutex;
};
class fooMultiThreadUserClass{
void usefoo(){
std::thread t1(&fooMultiThreadUserClass::useFooWorker, this);
std::thread t2(&fooMultiThreadUserClass::useFooWorker, this);
std::thread t3(&fooMultiThreadUserClass::useFooWorker, this);
std::thread t4(&fooMultiThreadUserClass::useFooWorker, this);
t1.join();
t2.join();
t3.join();
t4.join();
}
void useFooWorker(){
fooAttr.f();
}
foo fooAttr;
}
I know that fooMultiThreadUserClass will now be able to run foo without races in high performance, but will fooSingleThreadUserClass loose performance due to mutex overhead? I would be very intrested to know. Or should I derive fooCC from foo for concurrency purposes so fooSingleThreadUserClas can keep using foo without mutex, and fooMultiThreadUserClass use fooCC with mutexes, as shown below
class fooCC : public foo{
public:
foo(){
attr01 = 0;
}
void f(){ // I assume that foo::f() is now a virtual function.
attr01Mutex.lock();
foo::f();
attr01Mutex.unlock();
}
private:
std::mutex attr01Mutex;
};
Also assume that compiler optimization already took care of virtual dispatches. I would like an opinion wether I should use inhertance or simply put the mutex lock in the original class.
I've search through Stackoverflow already, but I guess my question is a little too specific.
Edit: Note, there doesn’t have to be just one argument, the question is meant to be abstract with a class of n argument.
Use an std::lock_guard
. The lock_guard
takes a mutex
in its constructor. During construction, the lock_guard
locks the mutex
. When the lock_guard
goes out of scope, its destructor automatically releases the lock.
class foo
{
private:
std::mutex mutex;
int attr01;
public:
foo() {
attr01 = 0;
}
void f(){
std::lock_guard<std::mutex> lock (mutex);
attr01 += 5;
}
};
You can put mutable
on the mutex
if you need to be able to lock or unlock the mutex
from const
functions. I usually leave mutable
off the mutex
until I specifically need it.
Will it lose performance? It depends. If you are calling the function a million times then maybe the overhead of creating the mutex
will become a problem (they are not cheap). If the function takes a long time to execute and it is called frequently by many threads, then perhaps the rapid blocking will hinder performance. If you can't pinpoint a specific concern, just use std::lock_guard
.
Hans Passant brings up a valid concern that is out-of-scope of your question. I think Herb Sutter (?) wrote about this in one of his website articles. Unfortunately I can't find it right now. To understand why multi-threading is so hard, and why locks on single data fields is "not enough", read a book on multi-threaded programming like C++ Concurrency in Action: Practical Multithreading