6

We can get the id of the main thread by calling std::this_thread::get_id() just at the start of the main function like this answer suggests. We can then store this id in a global variable and compare against a call of std::this_thread::get_id().

However, this forces us to change the main function. Is there a way to create a library function that does this? I was thinking about using a global variable initialized with std::this_thread::get_id() expression. Since global variables (variables with static duration) are initialized relatively early it is unlikely (but not impossible, see: deferred dynamic initialization) that threads are spawned before these variables are initialized.

I could also initialize the global variable with a helper function which enumerates all threads and picks the one with the earliest creation time (based on this answer).

I am very new to multithreading so any advice or guidance is extremely welcome.

Community
  • 1
  • 1
Martin Drozdik
  • 12,742
  • 22
  • 81
  • 146
  • 5
    Why? What difference does it make what thread any particular function call is running on? – Andrew Henle Nov 23 '15 at 11:25
  • 1
    what is the purpouse for that?, in your code you should know if a function is called on the main thread or not, easy as that. :) – Netwave Nov 23 '15 at 11:25
  • I'd really suggest sticking to the first answer and modify main. You could create a library which has like an `init` function you can call to main which does all sorts of stuff (including grabbing the main thread ID). That gives you one line of boilerplate to deal with in each main entry point. It doesn't get much more practical than that IMO -- relying on techniques to try to avoid this one-liner can get you in trouble. –  Nov 23 '15 at 11:26
  • 3
    @DanielSanchez Sometimes it's useful for sanity checks, e.g. It's not always so easy if you are designing a general-purpose library, e.g., to know what thread is going to call a function. The library in that case would be decoupled from any particular thread and its functions could potentially be called from any thread. –  Nov 23 '15 at 11:28
  • @AndrewHenle and Daniel: That is a good point, I will discuss this with my colleagues. – Martin Drozdik Nov 23 '15 at 11:29
  • It's a very rare case and probably the result of a compromise in thread-safety, but occasionally an interface function only makes sense to be called from a specific thread (main or not -- but something predetermined), yet it could be accessible to all. In that case there's no compile-time safety feature to prevent other threads from accessing this function. We could try to get all elaborate and try to find a way to reduce visibility of this function one thread, but sometimes it's useful to simply turn that into a run-time assertion. Simply `assert` that the current thread is the one expected. –  Nov 23 '15 at 11:39
  • Though typically in my case, it wasn't actually the main thread, per se. It was more like, "This object isn't thread safe. Make sure all of its methods are invoked by the thread that actually created it." So typically the thread ID would be captured in the ctor in my case, and used for debug-only sanity checks in its methods. –  Nov 23 '15 at 11:43
  • 5
    In Windows the process can outlive the main thread and spawn more threads. http://blogs.msdn.com/b/oldnewthing/archive/2010/08/27/10054832.aspx What if your library is loaded by a secondary thread *after* the main thread has been terminated? – ta.speot.is Nov 23 '15 at 11:58
  • 4
    You will get much better answers if you explain the actual problem you are trying to solve rather than asking for help implementing your chosen solution. – David Schwartz Nov 23 '15 at 11:59

4 Answers4

19

There is no such thing as main thread. There is a thread which was launched first, but all threads are first-class citizens. By tinkering with linker flags, I can easily create a program where the thread executing main() would not be the the thread launched first.

Rethink your design.

SergeyA
  • 61,605
  • 5
  • 78
  • 137
  • I almost agree. Without linker flags, in Windows, you can `ExitThread` in the thread that runs `WinMain`, and in POSIX, you can `pthread_exit` in the thread that runs `main`. The first thread may not exist anymore when a library is loaded. However, even with a raw program entry point in both systems, you can't avoid the fact that it runs **after** libraries have been loaded and initialized. – acelent Nov 24 '15 at 01:50
  • @PauloMadeira, loading and initializing libraries is a big topic, especially with lazy binding :) Luckily, I didn't mention that in my answer - I was only saying about the thread executing `main()`. – SergeyA Nov 24 '15 at 14:21
  • You're right, you can have a raw entry point that runs `main` or `WinMain` in another thread. – acelent Nov 24 '15 at 23:42
  • 1
    FYI "main thread" (aka "UI thread") is the terminology used on iOS/macOS. – Jyaif Aug 11 '17 at 18:41
  • @Jyaif see neither of those tags in the question. – SergeyA Aug 13 '17 at 13:29
  • Apparently this is different in Windows: Per [this Microsoft doc](https://learn.microsoft.com/en-us/windows/win32/api/debugapi/nf-debugapi-waitfordebugeventex), "Only the thread that created the process being debugged can call WaitForDebugEventEx". – Spencer Nov 04 '22 at 20:35
7

EDIT

This is not a solid way of getting the main thread's ID considering what @ta.speot.is and @David Schwartz said.

You could make a static variable somewhere that initializes with the current thread's ID.

const std::thread::id MAIN_THREAD_ID = std::this_thread::get_id();

And then somewhere else:

if (std::this_thread::get_id() == MAIN_THREAD_ID)
{
    std::cout << "main thread\n";
}
else
{
    std::cout << "not main thread\n";
}
Neijwiert
  • 985
  • 6
  • 19
  • 1
    Thank you. This was the first thing that I considered, however, the problem is that I do not have the guarantee that `MAIN_THREAD_ID` will be initialized in the main thread. Or do I??? – Martin Drozdik Nov 23 '15 at 11:31
  • See [this](http://stackoverflow.com/questions/1962880/is-c-static-member-variable-initialization-thread-safe) So yeah, it should be running on the same thread `main` runs on. – Neijwiert Nov 23 '15 at 11:55
  • Hm works for me in Visual Studio 2015 under Windows 10 in a normal Console application. – Neijwiert Nov 23 '15 at 12:03
  • This won't work. For one thing, thread IDs can be re-used. "Once a thread has finished, the value of std::thread::id may be reused by another thread." – David Schwartz Nov 23 '15 at 12:05
  • Isn't the thread id re-used only when the thread finishes its execution? Which is when the program ends, OP didn't mention persitence outside of life-time of application. – Neijwiert Nov 23 '15 at 12:08
  • @Neijwiert Why do you say "which is when the program ends"? A program doesn't terminate until it calls an exit function or all threads terminate. (Also, I can't find any specific guarantee that static objects are initialized by the same thread as calls `main`.) – David Schwartz Nov 23 '15 at 12:16
  • I retract my statement and it is undeed not a solid way of testing if it is the main thread. – Neijwiert Nov 23 '15 at 12:17
  • Think about a DLL that is loaded some time after the process is already running. Its initialization occurs in the thread that invoked `LoadLibrary` (or alike). Moreover, like @ta.speot.is says in the question's comments, in Windows you can exit the first thread without exiting the process (using the native `WinMain` entry point, not the C/C++'s `main` entry point), so what thread qualifies as the main thread after this? – acelent Nov 23 '15 at 13:28
  • @PauloMadeira, I can achieve the same effect in *Nix by tinkering with linker flags. – SergeyA Nov 23 '15 at 14:33
0

If you are using MFC. You can check AfxGetMainWnd(). If it return a pWnd then you have the (MFC) main thread. Disclaimer: If you have not manipulatued the m_pMainWnd Pointer.

Tom Tom
  • 1,127
  • 11
  • 25
0

Here's a solution for Linux:

#include <stdio.h>
#include <string>
#include <thread>
#include <unistd.h>
#include <sys/syscall.h>

void check_thread() {
    if (syscall(SYS_gettid) == getpid()) {
        printf("Hello from main thread\n");
    }
    else {
        printf("Hello from child thread\n");
    }
}

int main() {
    check_thread();
    std::thread t(check_thread);
    t.join();
}
itaych
  • 644
  • 5
  • 18