从线程获取线程ID


319

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

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

因此,我想知道Visual Studio如何获取线程的ID,例如,是否有办法获取ID为的线程的句柄2345

Answers:


437

GetThreadId返回给定本机线程的ID。有很多方法可以使其与托管线程一起使用,我敢肯定,您需要找到的只是线程句柄并将其传递给该函数。

GetCurrentThreadId 返回当前线程的ID。

GetCurrentThreadId从.NET 2.0开始不推荐使用:推荐的方式是Thread.CurrentThread.ManagedThreadId属性。


87
既然我找到了它,然后键入它,然后得知它已被弃用,那么当前的执行方法是Thread.CurrentThread.ManagedThreadId
James

3
由于您的应用程序重用了ManagedThreadId属性ID,因此ManagedThreadId并不是一种可靠的标识线程的方法。因此,在某些情况下,它不是线程的可靠标识符,您将遇到以下异常:“已经添加了具有相同密钥的项目。” 在线...创建线程时为其指定一个唯一的名称。
2012年

15
在这篇文章中有一些非常糟糕的建议。一些人建议使用“ ManagedThreadId”来标识线程。我编辑了帖子以删除建议-很少有人指出,线程ID有不同类型。托管线程ID与非托管线程ID不同,如果人们要复制并粘贴该代码,则可能会发生一些非常细微的同步错误。MSDN上有关Thread类的文档对此非常清楚。在班级上查看备注。
ShadowChaser 2013年

3
但是,您不会在ID上进行同步,而是使用诸如互斥锁之类的同步原语。这仅用于调试目的。
Blindy 2013年

11
我想发表此评论,以注意System.Threading.Thread.CurrentThread.ManagedThreadId至少在in中使用时无效SetWindowsHookEx。相反,我们必须从本地win32函数获取线程ID GetCurrentThreadId()
King King

82

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

这将是托管线程的ID。 ManagedThreadId是的成员,Thread因此您可以从任何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获得托管线程是一项艰巨的任务。

有关托管线程与非托管线程的更多详细信息,请参见此MSDN arcticle


4
为什么其他人都没有想到这个简单的答案?
Stefan Steinegger 2010年

2
这是行不通的。GetCurrentProcess()。Threads返回一个ProcessThreadCollection,它不能转换为Threads。我看不到简单的解决方法。
mafu 2011年

2
@ mafutrct,更新的答案。该属性实际上应该称为.ProcessThreads!谢谢。
badbod99 2011年

2
建议将此帖子改写,以使两个线程ID更加清楚。如果有人看不懂最后一句话,他们只会插入ManagedThreadId并尝试将其映射到ProcessThread.Id,从而创建havok。
ShadowChaser 2013年

1
我已经添加了指向有用的MSDN acticle的链接,以突出显示它们之间的区别。但是,问题与获取用于调试的线程ID(在本例中为ManagedThreadID)有关。我不认为用操作系统和托管线程之间差异的细节来使答案井井有条是很有用的。
badbod99

46

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

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

有关更多原因,请参阅配置程序的答案。


注意使用.NET 2.2的核心,需要注意的是AppDomain.GetCurrentThreadId(我通过MethodInfo的是过时的调用)返回托管线程ID(无用匹配Process.GetCurrentProcess()的线程集合。
brewmanz

32

要获取OS ID,请使用:

AppDomain.GetCurrentThreadId()

1
GetHashCode不一定是唯一的!并且不应使用它来标识线程。
Dror Helper

2
如果需要OS线程ID,则可以使用AppDomain.GetCurrentThreadId(),但理论上,多个.NET线程可以共享同一OS线程。保证Thread.GetHashCode()返回一个在整个进程范围内唯一的值,这可能是您想要的。
Mark Byers,2009年

3
该方法被标记为已弃用,并且有充分的理由。请查看我的答案和配置器的完整图片。
Paul Turner

3
好吧,这是获取OS线程ID的唯一方法。并将其标记为正确答案。即使那样,我也不会再依赖它了。
LolaRun

1
AppDomain.GetCurrentThreadId()已过时:AppDomain.GetCurrentThreadId 已弃用,因为当托管线程在上运行时,它不提供稳定的ID fibers (aka lightweight threads)。要获取托管线程的稳定标识符,请使用上的ManagedThreadId属性Thread。用法:Thread.CurrentThread.ManagedThreadId
Lijo Joseph

22

根据MSDN

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

因此,基本上,该Thread对象不一定与OS线程相对应-这就是为什么它没有公开本机ID的原因。


VS2010中的“调试/线程”窗口显示“托管线程ID”。我怎么能得到这个?
帕维尔·拉兹维洛夫斯基

1
使用ManagedThreadID属性msdn.microsoft.com/en-us/library/…。但是,这与OS线程ID不同。
配置器2010年

15

对于那些要入侵的人:

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

11

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

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

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

然后,使用Process.GetCurrentProcess()。Threads通过其ID来查找线程,以对该进程的线程进行迭代,并找到具有该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)将返回空引用。
FredrikMörk,2009年

我注意到,代码代码有一些错误-修复它现在就试试
德罗尔助手

1
我最终使用了将Win32 ID映射到托管线程的字典。
Contango 2012年

11

Windows 10下的偏移量是0x022C(x64位应用程序)和0x0160(x32位应用程序):

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

1
同样适用于带有SP1的Windows 7 x64。虽然不推荐。仅用于临时测试。
guan boshen

5

System.Threading.Thread.CurrentThread.Name

System.Threading.Thread.CurrentThread.ManagedThreadId

5

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

Visual Studio显示的ID实际上是操作系统线程ID。这是一样的托管线程ID几种答复的建议。

Thread类型确实包含一个名为的私有IntPtr成员字段DONT_USE_InternalThread,该成员字段指向基础OS结构。但是,由于这实际上是一个实现细节,因此不建议执行此IMO。而且名称名称表明您不应该依赖于此。


要使用GetThreadId,您需要一个句柄-从DONT_USE字段中获取。
配置器

我知道,但是正如我说的那样,您不能真正依靠托管线程直接映射到OS线程这一事实,所以我不会依靠它。
布赖恩·拉斯穆森

非常感谢您的澄清,并总结了问题。但是现在,如果多个托管线程可以与一个OS线程相对应(如配置程序所述-非常感谢),这意味着VS显示的是OS线程,而不是托管线程。
LolaRun

@OhrmaZd:是的,VS2005 / 2008在“线程”窗口中显示了托管线程的OS ID。VS2010B2实际上显示每个线程的操作系统和托管ID。
布赖恩·拉斯穆森

@Brian Rasmussen:现在,这是托管线程的标识!感谢您分享您的知识。
LolaRun

4

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

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

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


4
太好了,我刚刚将这个5岁的问题开放了一个小时,回来后看到“这个问题的一个新答案”:D
Ray

在另一条评论中暗示了这个答案,但这是我经过进一步研究后最终使用的答案。可能不是OP想要的。OP可能不再关心。可能对其他人有用。(并且至少基于参考源,这可能是获取线程ID的最有效方法。)
yoyo 2014年

好吧,我现在在不同的字段中,但是那时候,我们有一个线程的两个ID,本机线程的ID和托管线程的ID,一个属于另一个。 ID旨在标识线程,GetHashCodes具有其他实用程序,并且可能会冲突。如果我们必须使用GetHashCode
LolaRun

3
@yoyo冲突不会破坏字典的使用。它们被设计为具有较低的碰撞概率,而不是根本没有碰撞。如果将128位值散列为64位值,则每个散列值将有大约2 ^ 64个冲突。该字典设计为在极少数情况下发生冲突时具有回退算法
bradgonesurfing

2
@bradgonesurfing您绝对正确,而我之前的评论是错误的。字典性能会因哈希冲突而下降,但功能仍然正确。对于造成误导的评论,我深表歉意,感谢您指出这一点。
yoyo
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.