从线程中获取线程 ID

Posted

技术标签:

【中文标题】从线程中获取线程 ID【英文标题】:Getting the thread ID from a thread 【发布时间】:2010-12-13 08:22:27 【问题描述】:

例如在 C# 中调试线程时,可以看到每个线程的 ID。

我找不到以编程方式获取相同线程的方法。我什至无法获得当前线程的 ID(在 Thread.currentThread 的属性中)。

所以,我想知道 Visual Studio 如何获取线程的 ID,有没有办法获取 ID 为 2345 的线程的句柄,例如?

【问题讨论】:

【参考方案1】:

GetThreadId 返回给定本机线程的 ID。有办法让它与托管线程一起工作,我敢肯定,你只需要找到线程句柄并将其传递给该函数。

GetCurrentThreadId 返回当前线程的 ID。

GetCurrentThreadId 自 .NET 2.0 起已被弃用:推荐的方法是 Thread.CurrentThread.ManagedThreadId 属性。

【讨论】:

自从我找到了这个,输入了它,然后被告知它已弃用,目前的方法是 Thread.CurrentThread.ManagedThreadId ManagedThreadId 不是一种可靠的方法来识别线程,因为 ManagedThreadId 属性 id 被您的应用程序重用。因此在某些情况下它不是线程的可靠标识符,您将遇到异常:“已添加具有相同键的项目”。 at line... 在创建线程时给它一个唯一的名称。 这篇文章有一些非常糟糕的建议。一些人建议使用“ManagedThreadId”来识别线程。我编辑了帖子以删除推荐 - 很少有人指出有不同类型的线程 ID。托管线程 ID 与非托管线程 ID 不同,如果人们要复制和粘贴该代码,可能会出现一些非常微妙的同步错误。 Thread 类的 MSDN 文档对此非常清楚。查看班级级别的备注。 您不会在 ID 上进行同步,而是使用互斥锁等同步原语。这仅用于调试目的。 我想发表此评论以注意System.Threading.Thread.CurrentThread.ManagedThreadId 至少在SetWindowsHookEx 中使用时不起作用。相反,我们必须从本机 win32 函数 GetCurrentThreadId() 获取线程 ID。【参考方案2】:

例如在 C# 中调试线程时,可以看到每个线程的 ID。

这将是托管线程的 ID。 ManagedThreadIdThread 的成员,因此您可以从任何 Thread 对象中获取 Id。这将为您提供当前的ManagedThreadID:

Thread.CurrentThread.ManagedThreadId

要通过 OS 线程 ID (不是 ManagedThreadID) 获取 OS 线程,您可以尝试一下 linq。

int unmanagedId = 2345;
ProcessThread myThread = (from ProcessThread entry in Process.GetCurrentProcess().Threads
   where entry.Id == unmanagedId 
   select entry).First();

似乎没有办法枚举托管线程,并且 ProcessThread 和 Thread 之间没有关系,因此通过其 Id 获取托管线程是一个艰难的过程。

有关托管与非托管线程的更多详细信息,请参阅this MSDN article。

【讨论】:

为什么没有其他人想出这个简单的答案? 这不起作用。 GetCurrentProcess().Threads 返回一个 ProcessThreadCollection,它不能转换为 Threads。我没有看到一个简单的解决方法。 @mafutrct,更新了答案。该属性确实应该称为 .ProcessThreads!谢谢。 建议重写这篇文章,以更清楚地说明两个线程 id 不同。如果有人没有读到最后一句话,他们只会插入 ManagedThreadId 并尝试将其映射到 ProcessThread.Id,从而创建 havok。 我添加了一个指向有用的 MSDN 文章的链接,突出了不同之处。但是,问题与获取用于调试的线程 ID(在本例中为 ManagedThreadID)有关。我认为用操作系统和托管线程之间差异的细节来混淆答案是没有用的。【参考方案3】:

您可以使用已弃用的AppDomain.GetCurrentThreadId 来获取当前正在运行的线程的ID。此方法对 Win32 API 方法GetCurrentThreadID 使用 PInvoke,并将返回 Windows 线程 ID。

此方法被标记为已弃用,因为 .NET 线程对象不对应于单个 Windows 线程,因此 Windows 无法为给定的 .NET 线程返回稳定的 ID。

请参阅配置者的回答了解更多原因。

【讨论】:

CAUTION 使用 .Net Core 2.2,请注意 AppDomain.GetCurrentThreadId(我通过 MethodInfo 作为已过时调用)返回托管线程 ID(对于匹配 Process.GetCurrentProcess().Threads 集合无用。【参考方案4】:

要获取操作系统 ID,请使用:

AppDomain.GetCurrentThreadId()

【讨论】:

GetHashCode 不一定是唯一的!并且不应使用它来识别线程。 如果您想要操作系统线程 ID,您可以使用 AppDomain.GetCurrentThreadId(),但理论上多个 .NET 线程可以共享同一个操作系统线程。 Thread.GetHashCode() 保证返回一个在进程范围内唯一的值,这可能是您想要的。 该方法被标记为已弃用,并且有充分的理由。请查看我的答案和配置器以获得更全面的信息。 这是获得操作系统线程 ID 的唯一方法。这应该被标记为正确答案。即使我不会再依赖这个了。 AppDomain.GetCurrentThreadId() 已过时:AppDomain.GetCurrentThreadId 已被弃用,因为当托管线程在 fibers (aka lightweight threads) 上运行时,它不提供稳定的 Id。要获得托管线程的稳定标识符,请使用Thread 上的ManagedThreadId 属性。用法:Thread.CurrentThread.ManagedThreadId【参考方案5】:

根据MSDN:

操作系统的 ThreadId 没有 与托管的固定关系 线程,因为非托管主机可以 控制之间的关系 托管和非托管线程。 具体来说,一个复杂的主机可以 使用 CLR Hosting API 来安排 许多托管线程针对同一 操作系统线程,或移动一个 不同之间的托管线程 操作系统线程。

所以基本上,Thread 对象不一定对应于操作系统线程 - 这就是它没有暴露本机 ID 的原因。

【讨论】:

VS2010 中的调试/线程窗口显示“托管线程 ID”。我怎样才能得到这个? 使用 ManagedThreadID 属性 msdn.microsoft.com/en-us/library/… 。不过,这与操作系统线程 ID 不同。【参考方案6】:

对于那些即将破解:

    public static int GetNativeThreadId(Thread thread)
    
        var f = typeof(Thread).GetField("DONT_USE_InternalThread",
            BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.Instance);

        var pInternalThread = (IntPtr)f.GetValue(thread);
        var nativeId = Marshal.ReadInt32(pInternalThread, (IntPtr.Size == 8) ? 548 : 348); // found by analyzing the memory
        return nativeId;
    

【讨论】:

【参考方案7】:

要查找当前线程 ID,请使用 - `Thread.CurrentThread.ManagedThreadId'。 但在这种情况下,您可能需要当前的 win32 线程 id - 使用 pInvoke 通过此函数获取它:

[DllImport("Kernel32", EntryPoint = "GetCurrentThreadId", ExactSpelling = true)]
public static extern Int32 GetCurrentWin32ThreadId();

首先,您需要保存托管线程 id 和 win32 线程 id 连接 - 使用将 win32 id 映射到托管线程的字典。

然后通过它的 id 找到一个线程,使用 Process.GetCurrentProcess().Threads 遍历进程的线程并找到具有该 id 的线程:

foreach (ProcessThread thread in Process.GetCurrentProcess().Threads)

     var managedThread = win32ToManagedThread[thread.id];
     if((managedThread.ManagedThreadId == threadId)
     
         return managedThread;
     

【讨论】:

我相信 OP 正在询问线程的 OS ID,这与托管线程 ID 不同。 这段代码不起作用:Process.Threads返回一个ProcessThread对象的集合,这和Thread不一样(也不继承):(thread as Thread)会返回一个空引用. 我注意到代码代码有一些错误 - 修复它现在试试吧 我最终使用了一个将 win32 id 映射到托管线程的字典。【参考方案8】:

Windows 10下的偏移量为0x022C(x64-bit-Application)和0x0160(x32-bit-Application):

public static int GetNativeThreadId(Thread thread)

    var f = typeof(Thread).GetField("DONT_USE_InternalThread",
        BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.Instance);

    var pInternalThread = (IntPtr)f.GetValue(thread);
    var nativeId = Marshal.ReadInt32(pInternalThread, (IntPtr.Size == 8) ? 0x022C : 0x0160); // found by analyzing the memory
    return nativeId;

【讨论】:

也适用于带有 SP1 的 Windows 7 x64。虽然不推荐。仅用于临时测试。 我们不将其用于临时测试,而是用于使用我们的 iplus 框架 (iplus-framework.com) 的生产环境中的诊断目的【参考方案9】:

System.Threading.Thread.CurrentThread.Name

System.Threading.Thread.CurrentThread.ManagedThreadId

【讨论】:

【参考方案10】:

从托管代码中,您可以访问每个托管线程的 Thread 类型的实例。 Thread 封装了 OS 线程的概念,在当前的 CLR 中,托管线程和 OS 线程是一一对应的。但是,这是一个实现细节,将来可能会改变。

Visual Studio 显示的 ID 实际上是 OS 线程 ID。这与多个回复所建议的托管线程 ID 相同。

Thread 类型确实包含一个名为 DONT_USE_InternalThread 的私有 IntPtr 成员字段,它指向底层 OS 结构。然而,由于这实际上是一个实施细节,因此不建议追求这个 IMO。这个名字有点表明你不应该依赖这个。

【讨论】:

要使用 GetThreadId,您需要从 DONT_USE 字段获得的句柄。 我知道,但正如我所说,您不能真正指望托管线程直接映射到操作系统线程这一事实,所以我不会指望它。 非常感谢您的澄清和总结问题。但是现在如果多个托管线程可以对应于单个 OS 线程(正如配置器所说 - 他很感谢),这意味着 VS 显示的是 OS 线程而不是托管线程。 @OhrmaZd:是的,VS2005/2008 在线程窗口中显示托管线程的操作系统 ID。 VS2010B2 实际上显示每个线程的操作系统和托管 ID。 @Brian Rasmussen:现在这是托管线程的标识!感谢您分享您的知识。【参考方案11】:

您可以使用 Thread.GetHashCode,它返回托管线程 ID。如果您考虑 GetHashCode 的用途,这很有意义 - 它需要是对象(线程)的唯一标识符(例如字典中的键)。

reference source for the Thread class 在这里很有指导意义。 (当然,特定的 .NET 实现可能不是基于此源代码,但出于调试目的,我会抓住机会。)

GetHashCode“为需要快速检查对象相等性的算法提供此哈希码”,因此它非常适合检查线程相等性 - 例如断言特定方法正在您想要的线程上执行从调用。

【讨论】:

太棒了,我刚把这个 5 岁的问题打开了一个小时,回来看到“这个问题的 1 个新答案”:D 这个答案在另一条评论中有所暗示,但经过进一步研究后,我最终使用了这个答案。可能不是OP想要的。可能OP不再关心了。可能对其他人有用。 (并且至少基于参考来源,这可能是获取线程ID的最有效方式。) 好吧,我现在处于不同的领域,但当时,我们有两个线程 ID,一个是本地线程的 ID,一个是托管线程的 ID,一个属于另一个......主要是,ID 用于识别线程,GetHashCodes 有其他实用程序,并且可能会发生冲突。如果我们必须使用 GetHashCode,框架开发人员就不会实现 ID @yoyo 碰撞不会破坏字典的使用。它们被设计成具有低碰撞概率,而不是完全没有碰撞。如果您将 128 位值散列到 64 位值,则每个散列值将有大约 2^64 个冲突。字典设计为在极少数情况下发生碰撞时具有fallback algorithm。 @bradgonesurfing 你是绝对正确的,我之前的评论是错误的。字典性能会因哈希冲突而降低,但功能仍然正确。对于误导性评论,我深表歉意,感谢您指出这一点。

以上是关于从线程中获取线程 ID的主要内容,如果未能解决你的问题,请参考以下文章

从 sqlalchemy Session 获取 mysql 线程 id

java 中thread 获取线程id为啥默认从10开始

如何从 FreeBSD/Mac OS X 中的 Mutex 对象获取拥有的线程 ID

什么叫线程id号?怎么获取?

java中可以根据线程名而不是id获得想要的线程吗

在多线程情况下,如何获取当前线程id