SSteven
SSteven

Reputation: 753

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?

Upvotes: 1

Views: 1662

Answers (5)

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...

Upvotes: 0

Richard Hodges
Richard Hodges

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

Jens
Jens

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

Jens
Jens

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

Brighter side
Brighter side

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

Related Questions