I’m using std::thread to launch threads. Also, I need stats for the worker thread available at /proc/[pid]/tasks/[tid]
. I need tid to be able to monitor thread stats. I was wondering if there was a way to extract tid
from the parent thread. I know that syscall gettid() from the worker returns its id, but I want the threadId from the master and not the slave. Is there a way to extract tid
from the thread_id gor from std::thread.get_tid() ?
I believe there might be better ways of doing this, please suggest 🙂
UPDATE:
How can you get the Linux thread Id of a std::thread() this provides some information on getting tid from the worker, adds an overhead to the thread launch. For instance, std::thread t = std::thread(&wrapper);
t.get_id()
can be called from the launcher thread. I was/am looking if there was a to do the same thing from the main/launcher thread in a safe way.
Advertisement
Answer
All threads have a unique id:
std::thread::id this_id = std::this_thread::get_id();
You can store it in a variable when the program starts and it’ll be accessible from the other threads.
I understand what you mean when you say parent thread, but even though one thread gave birth to another, they are siblings.
if you want the master thread to be able to get the /proc
path to each worker thread, you could wrap the worker thread object in a class that, when it starts the actual thread, creates a path property that the master can later get.
An example:
#include <unistd.h> #include <sys/syscall.h> #include <sys/types.h> #include <condition_variable> #include <iostream> #include <mutex> #include <thread> // A base class for thread object wrappers class abstract_thread { public: abstract_thread() {} abstract_thread(const abstract_thread&) = delete; abstract_thread(abstract_thread&& rhs) : m_th(std::move(rhs.m_th)), m_terminated(rhs.m_terminated), m_cv{}, m_mtx{} {} abstract_thread& operator=(const abstract_thread&) = delete; abstract_thread& operator=(abstract_thread&& rhs) { terminate(); join(); m_th = std::move(rhs.m_th); m_terminated = rhs.m_terminated; return *this; } virtual ~abstract_thread() { // make sure we don't destroy a running thread object terminate(); join(); } virtual void start() { if(joinable()) throw std::runtime_error("thread already running"); else { std::unique_lock<std::mutex> lock(m_mtx); m_terminated = true; // start thread and wait for it to signal that setup has been done m_th = std::thread(&abstract_thread::proxy, this); m_cv.wait(lock, [this] { return m_terminated == false; }); } } inline bool joinable() const { return m_th.joinable(); } inline void join() { if(joinable()) { m_th.join(); } } inline void terminate() { m_terminated = true; } inline bool terminated() const { return m_terminated; } protected: // override if thread specific setup needs to be done before start() returns virtual void setup_in_thread() {} // must be overridden in derived classes virtual void execute() = 0; private: std::thread m_th{}; bool m_terminated{}; std::condition_variable m_cv{}; std::mutex m_mtx{}; void proxy() { { std::unique_lock<std::mutex> lock(m_mtx); setup_in_thread(); // call setup function m_terminated = false; m_cv.notify_one(); } execute(); // run thread code } }; // an abstract thread wrapper capable of returning its /proc path class proc_path_thread : public abstract_thread { public: // function to call from master to get the path const std::string& get_proc_path() const { return m_proc_path; } protected: void setup_in_thread() override { m_proc_path = std::move(std::string("/proc/")) + std::to_string(syscall(SYS_gettid)); } private: std::string m_proc_path{}; }; // two different thread wrapper classes. Just inherit proc_path_thread and implement // "execute()". Loop until terminated() is true (or you're done with the work) class AutoStartThread : public proc_path_thread { public: AutoStartThread() { start(); } private: void execute() override { while(!terminated()) { std::this_thread::sleep_for(std::chrono::milliseconds(500)); std::cout << std::this_thread::get_id() << " AutoStartThread runningn"; } } }; class ManualStartThread : public proc_path_thread { void execute() override { std::this_thread::sleep_for(std::chrono::milliseconds(100)); std::cout << std::this_thread::get_id() << " ManualStartThread runningn"; std::this_thread::sleep_for(std::chrono::milliseconds(100)); } }; int main() { AutoStartThread a; std::cout << a.get_proc_path() << "t// AutoStartThread, will have pathn"; ManualStartThread b; std::cout << b.get_proc_path() << "t// ManualStartThread not started, no pathn"; b.start(); std::cout << b.get_proc_path() << "t// ManualStartThread will now have a pathn"; b.join(); std::this_thread::sleep_for(std::chrono::milliseconds(1500)); // terminate() + join() is called automatically when abstract_thread descendants // goes out of scope: // // a.terminate(); // a.join(); }
Possible output:
/proc/38207 // AutoStartThread, will have path // ManualStartThread not started, no path /proc/38208 // ManualStartThread will now have a path 139642064209664 ManualStartThread running 139642072602368 AutoStartThread running 139642072602368 AutoStartThread running 139642072602368 AutoStartThread running 139642072602368 AutoStartThread running