53

This has been answered for Android, Objective C and C++ before, but apparently not for Python. How do I reliably determine whether the current thread is the main thread? I can think of a few approaches, none of which really satisfy me, considering it could be as easy as comparing to threading.MainThread if it existed.

Check the thread name

The main thread is instantiated in threading.py like this:

Thread.__init__(self, name="MainThread")

so one could do

if threading.current_thread().name == 'MainThread'

but is this name fixed? Other codes I have seen checked whether MainThread is contained anywhere in the thread's name.

Store the starting thread

I could store a reference to the starting thread the moment the program starts up, i.e. while there are no other threads yet. This would be absolutely reliable, but way too cumbersome for such a simple query?

Is there a more concise way of doing this?

Community
  • 1
  • 1
jonny
  • 4,264
  • 4
  • 22
  • 29

3 Answers3

79

The problem with threading.current_thread().name == 'MainThread' is that one can always do:

threading.current_thread().name = 'MyName'
assert threading.current_thread().name == 'MainThread' # will fail

Perhaps the following is more solid:

threading.current_thread().__class__.__name__ == '_MainThread'

Having said that, one may still cunningly do:

threading.current_thread().__class__.__name__ = 'Grrrr'
assert threading.current_thread().__class__.__name__ == '_MainThread' # will fail

But this option still seems better; "after all, we're all consenting adults here."

UPDATE:

Python 3.4 introduced threading.main_thread() which is much better than the above:

assert threading.current_thread() is threading.main_thread()

UPDATE 2:

For Python < 3.4, perhaps the best option is:

isinstance(threading.current_thread(), threading._MainThread)
s16h
  • 4,647
  • 1
  • 21
  • 33
  • 20
    `isinstance(threading.current_thread(), threading._MainThread)`? – iurii Jan 14 '15 at 14:42
  • That comment could be its own response :) – jonny Mar 19 '16 at 03:46
  • 2
    Is there any documentation about `_MainThread` though? Is that only part of one specific implementation? – jonny Mar 20 '16 at 22:24
  • 3
    Using attributes that start with an underscore is asking for trouble. Update 2 should say "For Python < 3.4, perhaps the best option is". Although for supporting multiple versions of python best may be to add your main script `if not hasattr(threading, 'main_thread'): threading.main_thread = legacy_main_thread_checker` with `legacy_main_thread_checker` using the "update 2" code of this answer. – Oliver Aug 30 '17 at 16:24
  • this method is not reliable if process forked, [new main thread no longer a `_MainThread` instance](https://github.com/python/cpython/blob/v3.6.3/Lib/threading.py#L1334). – georgexsh Nov 21 '17 at 11:31
26

The answers here are old and/or bad, so here's a current solution:

if threading.current_thread() is threading.main_thread():
    ...

This method is available since Python 3.4+.

wim
  • 338,267
  • 99
  • 616
  • 750
2

If, like me, accessing protected attributes gives you the Heebie-jeebies, you may want an alternative for using threading._MainThread, as suggested. In that case, you may exploit the fact that only the Main Thread can handle signals, so the following can do the job:

import signal
def is_main_thread():
    try:
        # Backup the current signal handler
        back_up = signal.signal(signal.SIGINT, signal.SIG_DFL)
    except ValueError:
        # Only Main Thread can handle signals
        return False
    # Restore signal handler
    signal.signal(signal.SIGINT, back_up)
    return True

Updated to address potential issue as pointed out by @user4815162342.

Community
  • 1
  • 1
asherbret
  • 5,439
  • 4
  • 38
  • 58
  • 2
    This answer has good intentions, but the presented code has a problem which makes it unfit for production. The `is_main_thread` function has a nasty side effect of actually changing the `SIGINT` behavior to libc's `SIG_DFL` instead of Python's normal `signal.default_int_handler`. In other words, Python will **no longer behave the same** after an invocation of `is_main_thread()` that returns true. After running it, pressing ^C will no longer cause the process to be interrupted with a `KeyboardInterrupt` (which prints a stack trace and runs `finally` blocks), but will immediately exit. – user4815162342 Mar 26 '17 at 18:36
  • @user4815162342 thank you for pointing that out. It seems to be possible to backup the current signal handler, and restore it before returning, thus keeping the signal handler intact. – asherbret Mar 26 '17 at 19:31