I know there are other posts that say you can create a control and then check the InvokeRequired property to see if the current thread is the main thread or not
InvokeRequired
If you're using Windows Forms or WPF, you can check to see if SynchronizationContext.Current is not null.
The main thread will get a valid SynchronizationContext set to the current context upon startup in Windows Forms and WPF.