【问题标题】:How to tell if a thread is the main thread in C#如何判断一个线程是否是C#中的主线程
【发布时间】:2010-03-03 19:51:16
【问题描述】:

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

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

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

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

有人知道更好的方法吗?在未来的运行时版本中,这种方式似乎很容易出错或中断。

【问题讨论】:

    标签: c# .net multithreading


    【解决方案1】:

    你可以这样做:

    // 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; }
    }
    

    编辑我意识到你也可以通过反射来做到这一点,这是一个 sn-p:

    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...
    }
    

    【讨论】:

      【解决方案2】:

      如果您使用的是 Windows 窗体或 WPF,您可以检查 SynchronizationContext.Current 是否不为空。

      在 Windows 窗体和 WPF 中启动时,主线程将获得一个有效的 SynchronizationContext 设置为当前上下文。

      【讨论】:

      • 这似乎是错误的。据我所知,SynchronizationContext.Current 在任何线程上都不能为空:docs.microsoft.com/en-us/dotnet/api/…
      • @ILIABROUDNO 错误,Current 在线程池线程上将为空。
      【解决方案3】:

      这要容易得多:

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

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

      if(Program.IsMainThread) ...
      

      解释:

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

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

      【讨论】:

      • 不幸的是,这个在 .NET core 中不再工作了
      【解决方案4】:

      在 WPF 应用程序中,这是另一种选择:

      if (App.Current.Dispatcher.Thread == System.Threading.Thread.CurrentThread)
      {
          //we're on the main thread
      }
      

      在 Windows 窗体应用程序中,只要至少有一个 Form 打开,这将起作用:

      if (Application.OpenForms[0].InvokeRequired)
      {
          //we're on the main thread
      }
      

      【讨论】:

        【解决方案5】:

        根据我的经验,如果您尝试从主线程以外的另一个线程创建对话框,那么 Windows 会变得很混乱,事情会开始变得疯狂。我尝试使用状态窗口执行此操作以显示后台线程的状态(以及许多其他时候有人会从后台线程抛出一个对话框 - 并且确实有一个消息循环) - 而 Windows 刚刚启动在程序中做“随机”的事情。我很确定对正在发生的事情进行了一些不安全的处理。单击表单和处理消息的错误线程有问题...

        所以,除了主线程之外,我永远不会从任何地方出现任何 UI。

        但是,为什么不简单地在启动时保存 CurrentThread,并将 ThreadID 与当前线程进行比较?

        -切特

        【讨论】:

          猜你喜欢
          • 2019-08-28
          • 2011-06-19
          • 2013-01-14
          • 1970-01-01
          • 1970-01-01
          • 2011-02-09
          • 1970-01-01
          • 2010-12-19
          • 1970-01-01
          相关资源
          最近更新 更多