Reputation: 753
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?
Upvotes: 1
Views: 1662
Reputation: 1
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.
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...
Upvotes: 0
Reputation: 69882
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;
/// ...
}
Upvotes: 1
Reputation: 9406
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;
}
Upvotes: 3
Reputation: 9406
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;
});
Upvotes: 0
Reputation: 402
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;
/// ...
}
Upvotes: 0