如何在C#中判断一个线程是否为主线程

52

还有其他的帖子说可以在 Windows Forms 中创建一个控件,然后检查 InvokeRequired 属性,以查看当前线程是否为主线程。

问题是您无法知道该控件本身是否在主线程上创建。

我正在使用以下代码来判断一个线程是否为主线程(启动进程的线程):

if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA ||
    Thread.CurrentThread.ManagedThreadId != 1 ||
    Thread.CurrentThread.IsBackground || Thread.CurrentThread.IsThreadPoolThread)
{
    // not the main thread
}

有没有人知道更好的方法?看起来这种方式可能容易出错或在将来的运行时版本中变得不兼容。

5个回答

56
您可以像这样做:
// Do this when you start your application
static int mainThreadId;

// In Main method:
mainThreadId = System.Threading.Thread.CurrentThread.ManagedThreadId;

// If called in the non main thread, will return false;
public static bool IsMainThread
{
    get { return System.Threading.Thread.CurrentThread.ManagedThreadId == mainThreadId; }
}

编辑我意识到你也可以使用反射来实现,以下是一个示例代码片段:

public static void CheckForMainThread()
{
    if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA &&
        !Thread.CurrentThread.IsBackground && !Thread.CurrentThread.IsThreadPoolThread && Thread.CurrentThread.IsAlive)
    {
        MethodInfo correctEntryMethod = Assembly.GetEntryAssembly().EntryPoint;
        StackTrace trace = new StackTrace();
        StackFrame[] frames = trace.GetFrames();
        for (int i = frames.Length - 1; i >= 0; i--)
        {
            MethodBase method = frames[i].GetMethod();
            if (correctEntryMethod == method)
            {
                return;
            }
        }
    }

    // throw exception, the current thread is not the main thread...
}

22

这似乎是错误的。据我所知,SynchronizationContext.Current 可能在任何线程上都不为 null:https://learn.microsoft.com/en-us/dotnet/api/system.threading.synchronizationcontext.current?view=net-5.0 - ILIA BROUDNO
@ILIABROUDNO 错了,Current 在线程池线程上将为 null。 - nawfal

17

在 WPF 应用程序中,这里有另一种选项:

if (App.Current.Dispatcher.Thread == System.Threading.Thread.CurrentThread)
{
    //we're on the main thread
}
在一个 Windows Forms 应用程序中,只要至少打开一个 Form,这将起作用:
if (!Application.OpenForms[0].InvokeRequired)
{
    //we're on the main thread, since invoking is NOT required
}

14

这更加容易:

static class Program
{
  [ThreadStatic]
  public static readonly bool IsMainThread = true;

//...
}

你可以从任何线程中使用它:

if(Program.IsMainThread) ...

解释:

IsMainThread 字段的初始化器被编译为静态构造函数,该构造函数在类第一次使用时运行(技术上,在任何静态成员的首次访问之前)。假设类在主线程上首次使用,则静态构造函数将被调用,并且该字段将在主线程上设置为 true。

由于该字段具有 [ThreadStatic] 属性,因此它在每个线程中具有独立的值。初始化器仅在访问类型的第一个线程中运行一次,因此该线程中的值为 true,但所有其他线程中该字段保持未初始化,值为 false


1
很遗憾,这个在.NET Core中已经不再起作用了。 - picrap

1
根据我的经验,如果你试图从主线程以外的另一个线程创建对话框,那么Windows会变得非常混乱,事情开始变得疯狂。我曾经试过用状态窗口显示后台线程的状态(还有很多其他人从后台线程中抛出对话框的时候 - 并且其中一个确实有消息循环),但是Windows在程序中开始做“随机”的事情。我相当确定某些不安全的处理正在进行中。我遇到了点击表单和错误线程处理消息的问题...

因此,我永远不会让任何UI从除主线程以外的任何地方弹出。

然而,为什么不在启动时保存当前线程,并将ThreadID与当前线程进行比较呢?

-Chert


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接