【问题标题】:How do I get the _real_ thread id in a CLR "friendly" way?如何以 CLR“友好”的方式获取 _real_ 线程 ID?
【发布时间】:2011-04-11 16:03:03
【问题描述】:

作为练习,我正在编写一些代码来显示进程中的 O/S 进程和 O/S 线程(就像 Sysinternals 进程浏览器所做的那样)。

我发现 .net 的 ManagedThreadId(s) 不是 O/S 线程 ID。经过一番阅读,我遇到了 AppDomain.GetCurrentThreadId()。不幸的是,该功能被标记为“过时”(这可能意味着将来“不可用”)。我发现的一种解决方案是使用 InteropServices 直接调用 Win32 GetCurrentThreadId。我对此很好,但感觉与 .net 理念背道而驰。

我的问题是:有没有一种 CLR“友好”的方式来获取当前线程的真实 id?

作为参考,这里有一个 sn-p 的代码,展示了我到目前为止所做的尝试。 // 1 和 // 2 显示正确的线程 id, // 3 和 // 4 试图以 CLR 友好的方式获取相同的信息(但它们不起作用。)

谢谢你的帮助,

约翰。

[DllImport("kernel32.dll")]
static extern int GetCurrentThreadId();

static void Main(string[] args)
{
  // AppDomain.GetCurrentThreadId() is "obsolete"

  int ThreadId1 = AppDomain.GetCurrentThreadId();   // 1 

  // not the ".net" way of doing things

  int ThreadId2 = GetCurrentThreadId();             // 2 

  // "no joy" attempts to get the same results I got above

  int ThreadId3 = Process.GetCurrentProcess().Threads[0].Id;   // 3
  int ThreadId4 = Thread.CurrentThread.ManagedThreadId;        // 4


  Console.WriteLine("ThreadId1: {0}, ThreadId2: {1}, ThreadId3: {2}, " + 
                    "ThreadId4: {3}",
                    ThreadId1, ThreadId2, ThreadId3, ThreadId4);
}

【问题讨论】:

  • 可能与 this question 重复。
  • 为什么您需要一种 CLR“友好”方式来获取一条只能执行 CLR“不友好”操作的 CLR“不友好”数据?
  • @Polity:我编写系统实用程序,其中大多数与 .net 无关。如果我在 .net 中编程,我希望对 .net “友好”,即使我感兴趣的信息与 .net 环境无关。
  • 好吧,我的观点仍然有效 :) 没有理由用钢制造汽车的一部分,而用木头制造另一部分。就像没有理由以 .NET 友好的方式获取本地线程 ID 一样,只能将其与本地系统 API 一起使用。在这种情况下使用 win32 的 GetCurrentThreadId 是完全有效的!

标签: c# winapi


【解决方案1】:

PInvoking 到 GetCurrentThreadId 是您最好的选择,它会为您提供正确的信息。

但是我必须警告您,CLR 不提供此信息的原因非常充分:对于托管代码而言,这几乎是一个完全无用的值。从 CLR 的角度来看,单个托管线程在其生命周期内由多个不同的本机线程支持是完全合法的。这意味着GetCurrentThreadId 的结果可以(并且将会)在线程的整个生命周期中发生变化。

在许多应用程序中,这不是可观察到的现象。在 UI 应用程序中,这实际上不会发生,因为它通常由 STA 线程支持,由于 COM 互操作问题,该线程更难(通常甚至非法)换出。如此多的开发人员对此一无所知。然而,在后台换出 MTA 线程非常容易,这通常是后台线程的执行上下文。

【讨论】:

  • 谢谢。我知道 .net 线程和 O/S 线程之间的区别(这就是我提到 Sysinternals 的 Process Explorer 的原因)。从您的回复中,我了解到除了 PInvoking 之外,没有 CLR 方法可以获取 O/S 的当前线程。在选择答案之前,我会等待其他建议。
  • @Hex440bx 我相当肯定从托管代码中获取本机 id 的所有方法在 2.0 中都已弃用。
【解决方案2】:

我不明白为什么人们会问“你为什么不……”。问题是如何获得 REAL(操作系统)线程 ID。它有一个重要的原因,它源于 Visual Studio 本身......它报告线程的结束不是使用托管 id,而是显然使用操作系统线程 id(例如“线程 0x5424 以代码 0 结束(0x0)。”)。为了能够完全跟踪/跟踪线程的使用和结束,以完美地验证您的线程处理,例如在异步方法上,它在新创建的线程上返回 [e.g. TCPListener.BeginAccept => new Socket on new thread],您需要本机线程 ID。在尽可能多地研究了这个问题的答案之后,在许多博客中,对我来说,似乎检索当前线程的本机 id 的唯一可靠方法是:

[DllImport("Kernel32", EntryPoint = "GetCurrentThreadId", ExactSpelling = true)]

public static extern Int32 GetCurrentWin32ThreadId();

...

int nativeThreadID = GetCurrentWin32ThreadId();

【讨论】:

  • 意见(“我不明白为什么...”)应保留为 cmets,而不是答案,以便为未来的用户提供尽可能清晰和简洁的答案。但是,我同意你的看法:)
【解决方案3】:

它已过时是有原因的;未来的想法是“实际”线程 ID 可能不是恒定的,或者可能在 .NET 线程之间共享。

【讨论】:

  • @Kieren:我不会去信息中“操纵”.net 中的某些东西,我只会在 O/S 级别使用它(例如,像 Process Explorer 那样。)跨度>
  • 矛盾的好像是你在找当前线程ID,有我上面提到的问题;关键是一个 .NET 线程不一定等于一个 O/S 线程
  • @Kieren:我理解你关于 .net 和 O/S 线程的观点。我要说的是,如果我想使用 C#(显然还有 .net)编写诸如 Process Explorer 之类的实用程序,是否可以在保持 .net 理念的同时提供在 O/S 级别准确的信息。
  • 那你为什么要调用 GetCurrentThread、GetCurrentProcess 等——这都与托管线程有关?您不是要枚举常规进程/线程(不会出现任何问题)吗?
  • @Kieren:因为(如 Process Explorer)我对 O/S 级别发生的事情感兴趣,而不是 .net 中发生的事情。在这种情况下,.net 是获取信息的手段,但不是终点(终点是 O/S 及其状态)
猜你喜欢
  • 2012-03-11
  • 2011-12-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多