My question is based on below sample of C++ code
#include <chrono>
#include <thread>
#include <mutex>
#include <iostream>
class ClassUtility
{
public:
ClassUtility() {}
~ClassUtility() {}
void do_something() {
std::cout << "do something called" << std::endl;
using namespace std::chrono_literals;
std::this_thread::sleep_for(1s);
}
};
int main (int argc, const char* argv[]) {
ClassUtility g_common_object;
std::mutex g_mutex;
std::thread worker_thread_1([&](){
std::cout << "worker_thread_1 started" << std::endl;
for (;;) {
std::lock_guard<std::mutex> lock(g_mutex);
std::cout << "worker_thread_1 looping" << std::endl;
g_common_object.do_something();
}
});
std::thread worker_thread_2([&](){
std::cout << "worker_thread_2 started" << std::endl;
for (;;) {
std::lock_guard<std::mutex> lock(g_mutex);
std::cout << "worker_thread_2 looping" << std::endl;
g_common_object.do_something();
}
});
worker_thread_1.join();
worker_thread_2.join();
return 0;
}
This is more of a question to get my understanding clear rather & get a sample usage of std::condition_variable
iff required.
I have 2 C++ std::thread
s which start up in main
method. Its a console app on osx
. So compiling it using clang. Both the threads use a common object of
ClassUtility
to call a method do some heavy task. For this sample code to explain the situation, both the threads run an infinite loop & close down only when
the app closes i.e. when I press ctrl+c
on the console.
Seek to know:
Is it correct if I jus use a std::lock_guard
on std::mutex
to synchronize or protect the calls made to the common_obejct
of ClassUtility
. Somehow, I seem
to be getting into trouble with this "just a mutex approach". None of the threads start if I lock gaurd the loops using mutex. Moreover, I get segfaults sometimes. Is this because they are lambdas ?
assigned to each thread ?
Is it better to use a std::condition_variable
between the 2 threads or lambdas to signal & synchronize them ? If yes, then how would the std::condition_variable
be used
here between the lambdas ?
Note: As the question is only to seek information, hence the code provided here might not compile. It is just to provide a real scenario
Remember, the lock_guard just calls .lock()
and injects call to .unlock()
to the end of the block. So
{
std::lock_guard<std::mutex> lock(g_mutex);
std::cout << "worker_thread_1 looping" << std::endl;
g_common_object.do_something();
}
is basically equivalent to:
{
g_mutex.lock();
std::cout << "worker_thread_1 looping" << std::endl;
g_common_object.do_something();
g_mutex.unlock();
}
except:
You are mutually excluding all of the loop body in each thread. There is nothing left that both threads could be actually doing in parallel. The main point of using threads is when each can work on separate set of objects (and only read common objects), so they don't have to be locked.
In the example code, you really should be locking only the work on common object; std::cout
is thread-safe on it's own. So:
{
std::cout << "worker_thread_1 looping" << std::endl;
{
std::lock_guard<std::mutex> lock(g_mutex);
g_common_object.do_something();
// unlocks here, because lock_guard injects unlock at the end of innermost scope.
}
}
I suppose the actual code you are trying to write does have something to actually do in parallel; just a thing to keep in mind.
Condition variables are for when you need one thread to wait until another thread does some specific thing. Here you are just making sure the two threads are not modifying the object at the same time and for that mutex
is sufficient and appropriate.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With