Unexpected output of multithreaded C++ program - c++

I'm studying concurrency in C++ and I'm trying to implement a multithreaded callback registration system. I came up with the following code, which is supposed to accept registration requests until an event occurs. After that, it should execute all the registered callbacks in order with which they were registered. The registration order doesn't have to be deterministic.
The code doesn't work as expected. First of all, it rarely prints the "Pushing callback with id" message. Secondly, it sometimes hangs (a deadlock caused by a race condition, I assume). I'd appreciate help in figuring out what's going on here. If you see that I overcomplicate some parts of the code or misuse some pieces, please also point it out.
#include <condition_variable>
#include <functional>
#include <iostream>
#include <mutex>
#include <queue>
#include <thread>
class CallbackRegistrar{
public:
void registerCallbackAndExecute(std::function<void()> callback) {
if (!eventTriggered) {
std::unique_lock<std::mutex> lock(callbackMutex);
auto saved_id = callback_id;
std::cout << "Pushing callback with id " << saved_id << std::endl;
registeredCallbacks.push(std::make_pair(callback_id, callback));
++callback_id;
callbackCond.wait(lock, [this, saved_id]{return releasedCallback.first == saved_id;});
releasedCallback.second();
callbackExecuted = true;
eventCond.notify_one();
}
else {
callback();
}
}
void registerEvent() {
eventTriggered = true;
while (!registeredCallbacks.empty()) {
releasedCallback = registeredCallbacks.front();
callbackCond.notify_all();
std::unique_lock<std::mutex> lock(eventMutex);
eventCond.wait(lock, [this]{return callbackExecuted;});
callbackExecuted = false;
registeredCallbacks.pop();
}
}
private:
std::queue<std::pair<unsigned, std::function<void()>>> registeredCallbacks;
bool eventTriggered{false};
bool callbackExecuted{false};
std::mutex callbackMutex;
std::mutex eventMutex;
std::condition_variable callbackCond;
std::condition_variable eventCond;
unsigned callback_id{1};
std::pair<unsigned, std::function<void()>> releasedCallback;
};
int main()
{
CallbackRegistrar registrar;
std::thread t1(&CallbackRegistrar::registerCallbackAndExecute, std::ref(registrar), []{std::cout << "First!\n";});
std::thread t2(&CallbackRegistrar::registerCallbackAndExecute, std::ref(registrar), []{std::cout << "Second!\n";});
registrar.registerEvent();
t1.join();
t2.join();
return 0;
}

This answer has been edited in response to more information being provided by the OP in a comment, the edit is at the bottom of the answer.
Along with the excellent suggestions in the comments, the main problem that I have found in your code is with the callbackCond condition variable wait condition that you have set up. What happens if releasedCallback.first does not equal savedId?
When I have run your code (with a thread-safe queue and eventTriggered as an atomic) I found that the problem was in this wait function, if you put a print statement in that function you will find that you get something like this:
releasedCallback.first: 0, savedId: 1
This then waits forever.
In fact, I've found that the condition variables used in your code aren't actually needed. You only need one, and it can live inside the thread-safe queue that you are going to build after some searching ;)
After you have the thread-safe queue, the code from above can be reduced to:
class CallbackRegistrar{
public:
using NumberedCallback = std::pair<unsigned int, std::function<void()>>;
void postCallback(std::function<void()> callback) {
if (!eventTriggered)
{
std::unique_lock<std::mutex> lock(mutex);
auto saved_id = callback_id;
std::cout << "Pushing callback with id " << saved_id << std::endl;
registeredCallbacks.push(std::make_pair(callback_id, callback));
++callback_id;
}
else
{
while (!registeredCallbacks.empty())
{
NumberedCallback releasedCallback;
registeredCallbacks.waitAndPop(releasedCallback);
releasedCallback.second();
}
callback();
}
}
void registerEvent() {
eventTriggered = true;
}
private:
ThreadSafeQueue<NumberedCallback> registeredCallbacks;
std::atomic<bool> eventTriggered{false};
std::mutex mutex;
unsigned int callback_id{1};
};
int main()
{
CallbackRegistrar registrar;
std::vector<std::thread> threads;
for (int i = 0; i < 10; i++)
{
threads.push_back(std::thread(&CallbackRegistrar::postCallback,
std::ref(registrar),
[i]{std::cout << std::to_string(i) <<"\n";}
));
}
registrar.registerEvent();
for (auto& thread : threads)
{
thread.join();
}
return 0;
}
I'm not sure if this does exactly what you want, but it doesn't deadlock. It's a good starting point in any case, but you need to bring your own implementation of ThreadSafeQueue.
Edit
This edit is in response to the comment by the OP stating that "once the event occurs, all the callbacks should be executed in [the] order that they've been pushed to the queue and by the same thread that registered them".
This was not mentioned in the original question post. However, if that is the required behaviour then we need to have a condition variable wait in the postCallback method. I think this is also the reason why the OP had the condition variable in the postCallback method in the first place.
In the code below I have made a few edits to the callbacks, they now take input parameters. I did this to print some useful information while the code is running so that it is easier to see how it works, and, importantly how the condition variable wait is working.
The basic idea is similar to what you had done, I've just trimmed out the stuff you didn't need.
class CallbackRegistrar{
public:
using NumberedCallback = std::pair<unsigned int, std::function<void(int, int)>>;
void postCallback(std::function<void(int, int)> callback, int threadId) {
if (!m_eventTriggered)
{
// Lock the m_mutex
std::unique_lock<std::mutex> lock(m_mutex);
// Save the current callback ID and push the callback to the queue
auto savedId = m_currentCallbackId++;
std::cout << "Pushing callback with ID " << savedId << "\n";
m_registeredCallbacks.push(std::make_pair(savedId, callback));
// Wait until our thread's callback is next in the queue,
// this will occur when the ID of the last called callback is one less than our saved callback.
m_conditionVariable.wait(lock, [this, savedId, threadId] () -> bool
{
std::cout << "Waiting on thread " << threadId << " last: " << m_lastCalledCallbackId << ", saved - 1: " << (savedId - 1) << "\n";
return (m_lastCalledCallbackId == (savedId - 1));
});
// Once we are finished waiting, get the callback out of the queue
NumberedCallback retrievedCallback;
m_registeredCallbacks.waitAndPop(retrievedCallback);
// Update last callback ID and call the callback
m_lastCalledCallbackId = retrievedCallback.first;
retrievedCallback.second(m_lastCalledCallbackId, threadId);
// Notify one waiting thread
m_conditionVariable.notify_one();
}
else
{
// If the event is already triggered, call the callback straight away
callback(-1, threadId);
}
}
void registerEvent() {
// This is all we have to do here.
m_eventTriggered = true;
}
private:
ThreadSafeQueue<NumberedCallback> m_registeredCallbacks;
std::atomic<bool> m_eventTriggered{ false};
std::mutex m_mutex;
std::condition_variable m_conditionVariable;
unsigned int m_currentCallbackId{ 1};
std::atomic<unsigned int> m_lastCalledCallbackId{ 0};
};
The main function is as above, except I am creating 100 threads instead of 10, and I have made the callback print out information about how it was called.
for (int createdThreadId = 0; createdThreadId < 100; createdThreadId++)
{
threads.push_back(std::thread(&CallbackRegistrar::postCallback,
std::ref(registrar),
[createdThreadId](int registeredCallbackId, int callingThreadId)
{
if (registeredCallbackId < 0)
{
std::cout << "Callback " << createdThreadId;
std::cout << " called immediately, from thread: " << callingThreadId << "\n";
}
else
{
std::cout << "Callback " << createdThreadId;
std::cout << " called from thread " << callingThreadId;
std::cout << " after being registered as " << registeredCallbackId << "\n";
}
},
createdThreadId));
}
I am not entirely sure why you want to do this, as it seems to defeat the point of having multiple threads, although I may be missing something there. But, regardless, I hope this helps you to understand better the problem you are trying to solve.

Experimenting with this code some more, I found out why the "Pushing callback with id " part was rarely printed. It's because the call to registrar.registerEvent from the main thread was usually faster than the calls to registerCallbackAndExecute from separate threads. Because of that, the condition if (!eventTriggered) was almost never fulfilled (eventTriggered had been set to true in the registerEvent method) and hence all calls to registerCallbackAndExecute were falling into the else branch and executing straightaway.
Then, the program sometimes also didn't finish, because of a race condition between registerEvent and registerCallbackAndExecute. Sometimes, registerEvent was being called after the check if (!eventTriggered) but before pushing the callback to the queue. Then, registerEvent completed instantly (as the queue was empty) while the thread calling registerCallbackAndExecute was pushing the callback to the queue. The latter thread then kept waiting forever for the event (that had already happened) to happen.

Related

Multiple threads waiting for all to finish till new work is started

I am trying to create a sort of threadpool that runs functions on separate threads and only starts a new iteration when all functions have finished.
map<size_t, bool> status_map;
vector<thread> threads;
condition_variable cond;
bool are_all_ready() {
mutex m;
unique_lock<mutex> lock(m);
for (const auto& [_, status] : status_map) {
if (!status) {
return false;
}
}
return true;
}
void do_little_work(size_t id) {
this_thread::sleep_for(chrono::seconds(1));
cout << id << " did little work..." << endl;
}
void do_some_work(size_t id) {
this_thread::sleep_for(chrono::seconds(2));
cout << id << " did some work..." << endl;
}
void do_much_work(size_t id) {
this_thread::sleep_for(chrono::seconds(4));
cout << id << " did much work..." << endl;
}
void run(const function<void(size_t)>& function, size_t id) {
while (true) {
mutex m;
unique_lock<mutex> lock(m);
cond.wait(lock, are_all_ready);
status_map[id] = false;
cond.notify_all();
function(id);
status_map[id] = true;
cond.notify_all();
}
}
int main() {
threads.push_back(thread(run, do_little_work, 0));
threads.push_back(thread(run, do_some_work, 1));
threads.push_back(thread(run, do_much_work, 2));
for (auto& thread : threads) {
thread.join();
}
return EXIT_SUCCESS;
}
I expect to get the output:
0 did little work...
1 did some work...
2 did much work...
0 did little work...
1 did some work...
2 did much work...
.
.
.
after the respective timeouts but when I run the program I only get
0 did little work...
0 did little work...
.
.
.
I also have to say that Im rather new to multithreading but in my understanding, the condition_variable should to the taks of blocking every thread till the predicate returns true. And in my case are_all_ready should return true after all functions have returned.
There are several ways to do this.
Easiest in my opinion would be a C++20 std::barrier, which says, "wait until all of N threads have arrived and are waiting here."
#include <barrier>
std::barrier synch_workers(3);
....
void run(const std::function<void(size_t)>& func, size_t id) {
while (true) {
synch_workers.arrive_and_wait(); // wait for all three to be ready
func(id);
}
}
Cruder and less efficient, but equally effective, would be to construct and join() new sets of three worker threads for each "batch" of work:
int main(...) {
std::vector<thread> threads;
...
while (flag_running) {
threads.push_back(...);
threads.push_back(...);
...
for (auto& thread : threads) {
thread.join();
}
threads.clear();
}
Aside
I'd suggest you revisit some core synchronization concepts, however. You are using new mutexes when you want to re-use a shared one. The scope of your unique_lock isn't quite right.
Now, your idea to track worker thread "busy/idle" state in a map is straightforward, but cannot correctly coordinate "batches" or "rounds" of work that must be begun at the same time.
If a worker sees in the map that two of three threads, including itself, are "idle", what does that mean? Is a "batch" of work concluding — i.e., two workers are waiting for a tardy third? Or has a batch just begun — i.e., the two idle threads are tardy and had better get to work like their more eager peer?
The threads cannot know the answer without keeping track of the current batch of work, which is what a barrier (or its more complex cousin the phaser) does under the hood.
As-is, your program has a crash (UB) due to concurrent access to status_map.
When you do:
void run(const function<void(size_t)>& function, size_t id)
{
...
mutex m;
unique_lock<mutex> lock(m);
...
status_map[id] = false;
the locks created are local variables, one per thread, and as such independent. So, it doesn't prevent multiple threads from writing to status_map at once, and thus crashing. That's what I get on my machine.
Now, if you make the mutex static, only one thread can access the map at once. But that also makes it so that only one thread runs at once. With this I see 0, 1 and 2 running, but only once at a time and a strong tendency for the previous thread to have run to run again.
My suggestion, go back to the drawing board and make it simpler. All threads run at once, single mutex to protect the map, only lock the mutex to access the map, and ... well, in fact, I don't even see the need for a condition variable.
e.g. what is wrong with:
#include <thread>
#include <iostream>
#include <vector>
using namespace std;
vector<thread> threads;
void do_little_work(size_t id) {
this_thread::sleep_for(chrono::seconds(1));
cout << id << " did little work..." << endl;
}
void do_some_work(size_t id) {
this_thread::sleep_for(chrono::seconds(2));
cout << id << " did some work..." << endl;
}
void do_much_work(size_t id) {
this_thread::sleep_for(chrono::seconds(4));
cout << id << " did much work..." << endl;
}
void run(const function<void(size_t)>& function, size_t id) {
while (true) {
function(id);
}
}
int main() {
threads.push_back(thread(run, do_little_work, 0));
threads.push_back(thread(run, do_some_work, 1));
threads.push_back(thread(run, do_much_work, 2));
for (auto& thread : threads) {
thread.join();
}
return EXIT_SUCCESS;
}

Multiple Conditional Variables : threads out of sync problem

I have a thread that is doing "work", it is supposed to report progress when conditional variable notifies it. This thread is waiting for conditional variables.
Other thread is waiting for a x amount of milliseconds and then notifies conditional variable to proceed.
I have 5 conditional variables (this is an exercise for school) and once each gets notified work progress is supposed to be reported:
Problem im having is that thread 2, the one that is supposed to notify thread 1, goes through all 5 checkPoints and notifies only once in the end. So I end up in a situation where progress is at 20% in the end and thread 1 is waiting for another notify but thread 2 has finished all notifies.
Where is flaw in my implementation of this logic?
Code below:
#include <condition_variable>
#include <functional>
#include <iostream>
#include <mutex>
#include <thread>
using namespace std;
class Program {
public:
Program() {
m_progress = 0;
m_check = false;
}
bool isWorkReady() { return m_check; }
void loopWork() {
cout << "Working ... : " << endl;
work(m_cv1);
work(m_cv2);
work(m_cv3);
work(m_cv4);
work(m_cv5);
cout << "\nFinished!" << endl;
}
void work(condition_variable &cv) {
unique_lock<mutex> mlock(m_mutex);
cv.wait(mlock, bind(&Program::isWorkReady, this));
m_progress++;
cout << " ... " << m_progress * 20 << "%" << endl;
m_check = false;
}
void checkPoint(condition_variable &cv) {
lock_guard<mutex> guard(m_mutex);
cout << " < Checking >" << m_progress << endl;
this_thread::sleep_for(chrono::milliseconds(300));
m_check = true;
cv.notify_one();
}
void loopCheckPoints() {
checkPoint(m_cv1);
checkPoint(m_cv2);
checkPoint(m_cv3);
checkPoint(m_cv4);
checkPoint(m_cv5);
}
private:
mutex m_mutex;
condition_variable m_cv1, m_cv2, m_cv3, m_cv4, m_cv5;
int m_progress;
bool m_check;
};
int main() {
Program program;
thread t1(&Program::loopWork, &program);
thread t2(&Program::loopCheckPoints, &program);
t1.join();
t2.join();
return 0;
}
The loopCheckPoints() thread holds a lock for some time, sets m_check then releases the lock and immediately goes on to grab the lock again. The loopWork() thread may not have woken up in between to react to the m_check change.
Never hold locks for long times. Be as quick as possible. If you can't get the program to work without adding sleeps, you have a problem.
One way to fix this would be to check that the worker has actually set m_check back to false:
void work(condition_variable& cv) {
{ // lock scope
unique_lock<mutex> mlock(m_mutex);
cv.wait(mlock, [this] { return m_check; });
m_progress++;
cout << " ... " << m_progress * 20 << "%" << endl;
m_check = false;
}
// there's no need to hold the lock when notifying
cv.notify_one(); // notify that we set it back to false
}
void checkPoint(condition_variable& cv) {
// if you are going to sleep, do it without holding the lock
// this_thread::sleep_for(chrono::milliseconds(300));
{ // lock scope
lock_guard<mutex> guard(m_mutex);
cout << "<Checking> " << m_progress << endl;
m_check = true;
}
cv.notify_one(); // no need to hold the lock here
{
// Check that m_check is set back to false
unique_lock<mutex> mlock(m_mutex);
cv.wait(mlock, [this] { return not m_check; });
}
}
Where is flaw in my implementation of this logic?
cv.notify_one does not require, that the code after cv.wait(mlock, bind(&Program::isWorkReady, this)); continues immediatly, so it is perfectly valid that multiple checkPoint are exectued, before the code continues after cv.wait.
But after you the cv.wait you set m_check = false; to false, so if there is no further checkPoint execution remaining, that will set m_check = true;, your work function becomes stuck.
Instead of m_check being a bool you could think about making it a counter, that is incremented in checkPoint and decremented in work.

C++11: Publisher/Consumer pattern does not finish unless publisher sleeps

In C++, I am trying to get a handle for the publisher/consumer pattern using a condition_variable. This is loosely the template I have seen online:
#include <iostream>
#include <thread>
#include <string>
#include <mutex>
#include <queue>
#include <condition_variable>
#include <chrono>
using namespace std;
mutex m;
queue<string> que;
condition_variable cond;
void write(string &&msg) {
unique_lock<mutex> locker(m);
que.push(msg);
locker.unlock();
cond.notify_one();
this_thread::sleep_for(chrono::milliseconds(1));
}
void read() {
while (true) {
unique_lock<mutex> locker(m);
cond.wait(locker);
if (!que.empty()) {
cout << que.front() << endl;
que.pop();
}
locker.unlock();
}
}
void publisher(string &msg) {
for (int i = 0; i < 100; ++i)
write("Publisher: " + msg + ", " + to_string(i));
}
int main() {
string msg = "Hello";
thread pub_thread(publisher, std::ref(msg));
/* The main thread will print the publisher's messages to cout. */
read();
/* Make the main thread wait for the publisher to finish. */
pub_thread.join(); //
return 0;
}
The thing I do not understand is the sleep_for call on the publisher thread. I understand that this is there simply to simulate a "real life" scenario in which the publisher will not be spitting out messages so fast. However, curiously, if I comment out that line, the code does not run to completion. Why is that?
Furthermore, I tried setting the sleep_for time to 0 with the same effect. It seems that the published fundamentally needs to sleep, but I do not understand why. To get more specific, the code should print out 100 messages. If I let the code sleep for 1 ms, then all 100 messages are printed. If I do not, then I only see about 10 messages before the code freezes. It seems like there is a deadlock occurring.
Bonus points if there is a better pattern which avoids having to put the publisher to sleep...
I am aware that in practice you need to have a strategy for stopping the main thread, like a poison pill. I have intentionally omitted this to focus on the present discussion.
EDIT
Hmmm. If I put in a block to handle spurious wakeup, then that fixes the problem. But that still does not explain why the original code failed.
Here is an improved read function:
void read() {
while (true) {
unique_lock<mutex> locker(m);
cond.wait(locker, [&](){ return !que.empty(); });
cout << que.front() << endl;
que.pop();
locker.unlock();
}
}
You need to handle the situation when the publisher is publishing faster than the consumer consuming.
When that happens, the consumer will miss condition_variable triggers. Remember that notify calls do not accumulate.
Change the consumer to consume all available messages once it wakes up:
if (!que.empty()) → while (!que.empty())
Like this:
void read() {
while (true) {
unique_lock<mutex> locker(m);
cond.wait(locker);
while (!que.empty()) {
cout << que.front() << endl;
que.pop();
}
locker.unlock();
}
}
You have two problems: one is that the publisher is under no obligation to yield to the reader - or to pause long enough for the reader to successfully get lock the mutex - unless you make it.
The second is that your reader is anyway incorrect:
void read() {
while (true) {
unique_lock<mutex> locker(m);
cond.wait(locker);
if (!que.empty()) {
cout << que.front() << endl;
que.pop();
}
}
}
This assumes you get one "wakeup event" per element pushed onto the queue, because it consumes exactly one element per wakeup. But that's not how condition variables work.
It's entirely possible for this sequence to happen:
publisher adds item 1
publisher signals condvar
publisher adds item 2
publisher signals condvar
reader wakes from condvar wait
reader consumes item 1
In this case the publisher will add 100 items, and signal 100 times, but the reader will only wake 99 times and therefore consume at most 99 items.
The correct code should be something like this:
void read() {
unique_lock<mutex> locker(m);
while (true) {
// don't wait if we don't have to
while (que.empty()) {
cond.wait(locker);
}
// consume everything we can
while (!que.empty()) {
cout << que.front() << endl;
que.pop();
}
}
}
Using the predicate achieves roughly the same thing (I just wrote all the logic out explicitly for clarity) - the second while isn't there in your edit, but looping round and skipping the first while is a slightly more expensive way to get the same behaviour.
Also, there's no need to thrash the mutex on every iteration - the condition variable already (un)locks it as required.

Identifying a thread

To identify a thread, we must do the following:
1) Get and save its id.
2) Within a function running in the thread, get the thread's id again and compare it with the saved id.
Eg: http://coliru.stacked-crooked.com/a/8f608dff835f96d0
(The program is from Josuttis' book "The C++ Standard Library", 2nd Ed.)
thread::id master_tid {};
/// declarations
void doSomething();
int main()
{
thread master {doSomething};
master_tid = master.get_id();
thread slave {doSomething};
/// ...
/// join with threads
master.join();
slave.join();
cout << "done" << endl;
}
void doSomething()
{
if (this_thread::get_id() == master_tid)
cout << "master thread ..."
<< endl;
else
cout << "NOT master thread ..."
<< endl;
/// ...
}
The output is:
master thread ...
NOT master thread ...
done
However, the above scheme works only IF there is no delay between invoking the doSomething() function and saving the master thread’s id.
If such a delay is introduced, the doSomething() function won't be able to discriminate between the master thread and any other, since, when it runs, the master thread's id hasn't yet been saved in the referenced variable.
Eg: http://coliru.stacked-crooked.com/a/0bff325f872ba9c2
thread::id master_tid {};
/// declarations
void doSomething();
int main()
{
thread master {doSomething};
thread slave {doSomething};
/// delay
this_thread::sleep_for(seconds {1});
master_tid = master.get_id();
/// ...
/// join with threads
master.join();
slave.join();
cout << "done" << endl;
}
void doSomething()
{
/// ...
}
Now, because of the delay, the output is as follows:
NOT master thread ...
NOT master thread ...
done
Therefore, I'd like to ask, how can we make this scheme work perfectly? Do we need to use condition variables to communicate between the main thread and the other threads, indicating if the thread id has been saved?
Or, is there some simpler way?
All threads have to wait until the master_tid is assigned. Instead of manual sleep_for, which is error prone and will eventually lead to bugs in your program, you should use a synchronization mechanism. In your case, where you want all threads wait for a condition, you can use a condition variable. However, I would just pass different functions to master and slave, or pass a parameter.
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex m;
std::condition_variable cv;
thread::id master_tid {};
bool ready = false;
/// declarations
void doSomething() {
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, []{return ready;});
// master_tid is now assigned
if (this_thread::get_id() == master_tid)
cout << "master thread ..."
<< endl;
else
cout << "NOT master thread ..."
<< endl;
}
int main()
{
thread master {doSomething};
thread slave {doSomething};
{
std::lock_guard<std::mutex> lk(m);
ready = true;
master_tid = master.get_id();
}
cv.notify_all();
/// ...
/// join with threads
master.join();
slave.join();
cout << "done" << endl;
}
This problem is trivially solved with std::future.
example:
#include <future>
#include <thread>
#include <functional>
#include <iostream>
/// declarations
void doSomething(std::shared_future<std::thread::id> future_master_thread);
int main()
{
std::promise<std::thread::id> promise_master_thread;
auto shared_master_thread = promise_master_thread.get_future().share();
std::thread master {std::bind(doSomething, shared_master_thread)};
promise_master_thread.set_value(master.get_id());
std::thread slave {std::bind(doSomething, shared_master_thread)};
/// ...
/// join with threads
master.join();
slave.join();
std::cout << "done" << std::endl;
}
void doSomething(std::shared_future<std::thread::id> future_master_thread)
{
if (std::this_thread::get_id() == future_master_thread.get())
std::cout << "master thread ..."
<< std::endl;
else
std::cout << "NOT master thread ..."
<< std::endl;
/// ...
}
In fact your problem is that you crate 2 threads and you compare their id to a none initialized value.
Indeed we have a look to the scheduling
--------------------------------------------> main time
| | | |
master slave sleep attribute id
--------------------------------------------> master time
|
do_something
--------------------------------------------> slave time
|
do_something
A way to make them able to see if they are master or salve is to use a bool that tells is the master or the salve has been identified.
int main()
{
bool know_master = false;
thread master {doSomething};
thread slave {doSomething};
/// delay
this_thread::sleep_for(seconds {1});
master_tid = master.get_id();
know_master = true;
/// ...
/// join with threads
master.join();
slave.join();
cout << "done" << endl;
}
void doSomething()
{
while (! know_master) {
//sleep here
}
if (this_thread::get_id() == master_tid)
cout << "master thread ..."
<< endl;
else
cout << "NOT master thread ..."
<< endl;
/// ...
}
I am not sure I understand the question completely, but isn't it possible to pass the executed function an additional parameter which defines if it the master thread or not?
void doSomething(bool isMaster) {
if (isMaster)
cout << "master thread ..."
<< endl;
else
cout << "NOT master thread ..."
<< endl;
}
std::thread master = std::thread(doSomething, true);
std::thread slave = std::thread(doSomething, false);
Personally I would split doSomething into a general part and a specific part and then either create special functions for master and slave or define it in a lambda.
std::thread master = std::thread([]() {
doMasterStuff();
doSomething();
});
std::thread slave([]() {
doSlaveStuff();
doSomething();
});
Or I would use the template method pattern
class ThreadFunction {
private:
void doSpecific() = 0;
void doSomething() { ... }
public:
void operator()() {
doSpecific();
doSomething();
}
};
class MasterThread: public ThreadFunc {
void doSpecific() {
cout << "master thread ..."
<< endl;
}
};
Or create a class which gets the specific part as a "strategy".
template<typename T>
class ThreadFunc2 {
private:
T specific;
void doSomething() { ... };
public:
ThreadFunc2(T s): specific( std::move(s) ) {}
void operator()() {
specific();
doSomething();
}
};
std::thread master([]() {
cout << "master thread ..." << endl;
});
Identifying a thread on Linux
In addition to other answers, on Linux (specifically), you could identify a thread by using first pthread_setname_np(3) (near the start of the thread's function) and pthread_getname_np afterwards. You can also get a unique tid with the gettid(2) syscall (which returns some unique integer).
The disadvantage of pthread_getname_np is that you have to call pthread_setname_np once before (for your own explicitly created threads, it is easy; for threads created by some library, it is harder).
The disadvantage of gettid is that it is not wrapped by the C library. However, that wrapping is trivial to code:
static inline pid_t mygettid(void) { return syscall(SYS_gettid, 0L); }
You need syscall(2) and <sys/syscall.h>
Both pthread_getname_np and gettid may be Linux specific, but they can be used to uniquely identify your thread.
general hints about threads
As a rule of thumb, you'll better pass some thread-unique data at thread creation time e.g. to pthread_create(3) or as explicit (second and more) argument to std::thread constructor, notably if you want to use pthread_setname_np in your thread's function (and in most other cases).
So you would declare void doSomething(int); and do construct your threads using
thread master {doSomething,10};
thread slave {doSomething,42};
The choice of 10 and 42 is arbitrary, and you might declare instead something like void doSomething(std::string) and then have thread master {doSomething, std::string{"masterth"}}; etc...

Resolve deadlock issue, waiting in the main thread for multiple worker threads to finish (C++11)

I'm trying to write a program with c++11 in which multiple threads are run, and, during each cycle the main thread will wait for each thread to be finished. The program below is a testing program for this concept.
Apparently I'm missing something trivial in my implementation as it looks like I'm experiencing a deadlock (Not always, just during some random runs).
#include <iostream>
#include <stdio.h>
#include <thread>
#include <chrono>
#include <condition_variable>
#include <mutex>
using namespace std;
class Producer
{
public:
Producer(int a_id):
m_id(a_id),
m_ready(false),
m_terminate(false)
{
m_id = a_id;
m_thread = thread(&Producer::run, this);
// ensure thread is available before it is started
this_thread::sleep_for(std::chrono::milliseconds(100));
}
~Producer() {
terminate();
m_thread.join();
}
void start() {
//cout << "start " << m_id << endl;
unique_lock<mutex> runLock(m_muRun);
m_ready = true;
runLock.unlock();
m_cond.notify_all();
}
void wait() {
cout << "wait " << m_id << endl;
unique_lock<decltype(m_muRun)> runLock(m_muRun);
m_cond.wait(runLock, [this]{return !m_ready;});
}
void terminate() {
m_terminate = true;
start();
}
void run() {
do {
unique_lock<decltype(m_muRun)> runLock(m_muRun);
m_cond.wait(runLock, [this]{return m_ready;});
if (!m_terminate) {
cout << "running thread: " << m_id << endl;
} else {
cout << "exit thread: " << m_id << endl;
}
runLock.unlock();
m_ready = false;
m_cond.notify_all();
} while (!m_terminate);
}
private:
int m_id;
bool m_ready;
bool m_terminate;
thread m_thread;
mutex m_muRun;
condition_variable m_cond;
};
int main()
{
Producer producer1(1);
Producer producer2(2);
Producer producer3(3);
for (int i=0; i<10000; ++i) {
cout << i << endl;
producer1.start();
producer2.start();
producer3.start();
producer1.wait();
producer2.wait();
producer3.wait();
}
cout << "exit" << endl;
return 0;
}
The program's output when the deadlock is occurring:
....
.......
running thread: 2
running thread: 1
wait 1
wait 2
wait 3
running thread: 3
Looking at the program's output when the deadlock occurs, I suspect the bottleneck of the program is that sometimes the Producer::wait function is called, before the corresponding thread is actually started, i.e. the command Producer::start should have triggered the start, a.k. unlocking of the mutex, however it is not yet picked up by the thread's run method (Producer::run), (NB: I'm not 100% sure of this!). I'm a bit lost here, hopefully somebody can provide some help.
You have race condition in this code:
runLock.unlock();
m_ready = false;
m_ready variable must be always protected by mutex for proper synchronization. And it is completely unnecessary to wait for thread to start this_thread::sleep_for() - proper synchronization would take care of that as well so you can simply remove that line. Note this is pretty inefficient way of doing proper multithreading - there should be thread pool instead of individual object with separate mutex and condition variable each.