Abrufen der Thread-ID von einem Thread

319

In C # können Sie beispielsweise beim Debuggen von Threads die ID jedes Threads sehen.

Ich konnte keinen Weg finden, programmgesteuert denselben Thread zu erhalten. Ich konnte nicht einmal die ID des aktuellen Threads (in den Eigenschaften von Thread.currentThread) erhalten.

Ich frage mich also, wie Visual Studio die IDs der Threads erhält. Gibt es beispielsweise eine Möglichkeit, das Handle des Threads mit der ID 2345abzurufen?

LolaRun
quelle

Antworten:

437

GetThreadIdGibt die ID eines bestimmten nativen Threads zurück. Es gibt Möglichkeiten, wie es mit verwalteten Threads funktioniert. Ich bin sicher, alles, was Sie finden müssen, ist das Thread-Handle und übergeben Sie es an diese Funktion.

GetCurrentThreadId Gibt die ID des aktuellen Threads zurück.

GetCurrentThreadIdwurde ab .NET 2.0 veraltet: Der empfohlene Weg ist die Thread.CurrentThread.ManagedThreadIdEigenschaft.

Blind
quelle
87
Da ich dies gefunden, getippt und dann erfahren habe, dass es veraltet ist, ist der aktuelle Weg, dies zu tun, Thread.CurrentThread.ManagedThreadId
James
3
ManagedThreadId ist kein robuster Ansatz zum Identifizieren von Threads, da die ManagedThreadId-Eigenschafts-IDs von Ihrer App wiederverwendet werden. Daher ist es in einigen Szenarien keine zuverlässige Kennung für Threads, und es tritt die Ausnahme auf: "Ein Element mit demselben Schlüssel wurde bereits hinzugefügt." at line ... Geben Sie dem Thread beim Erstellen einen eindeutigen Namen.
Forer
15
In diesem Beitrag gibt es einige sehr schlechte Ratschläge. Einige Leute empfehlen, "ManagedThreadId" zu verwenden, um einen Thread zu identifizieren. Ich habe den Beitrag bearbeitet, um die Empfehlung zu entfernen. Nur wenige haben darauf hingewiesen, dass es verschiedene Arten von Thread-IDs gibt. Verwaltete Thread-IDs sind nicht dasselbe wie nicht verwaltete Thread-IDs. Wenn Benutzer diesen Code kopieren und einfügen, können einige sehr subtile Synchronisationsfehler auftreten. Die Dokumentation zu MSDN für die Thread-Klasse ist diesbezüglich sehr klar. Sehen Sie sich die Anmerkungen auf Klassenebene an.
ShadowChaser
3
Sie synchronisieren jedoch nicht mit IDs, sondern verwenden Synchronisationsprimitive wie Mutexe. Dies dient nur zu Debugging-Zwecken.
Blindy
11
Ich möchte diesen Kommentar posten, um festzustellen, dass dies System.Threading.Thread.CurrentThread.ManagedThreadIdzumindest bei Verwendung in a nicht funktioniert SetWindowsHookEx. Stattdessen müssen wir die Thread-ID von der nativen win32-Funktion abrufen GetCurrentThreadId().
König König
82

In C # können Sie beispielsweise beim Debuggen von Threads die ID jedes Threads sehen.

Dies sind die IDs der verwalteten Threads. ManagedThreadIdist ein Mitglied von, Threadsodass Sie die ID von jedem Thread- Objekt abrufen können . Dadurch erhalten Sie die aktuelle ManagedThreadID :

Thread.CurrentThread.ManagedThreadId

Um einen OS-Thread anhand seiner OS-Thread-ID (nicht ManagedThreadID) zu erhalten , können Sie ein wenig linq ausprobieren.

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

Es scheint, dass es keine Möglichkeit gibt, die verwalteten Threads und keine Beziehung zwischen ProcessThread und Thread aufzulisten. Daher ist es schwierig, einen verwalteten Thread anhand seiner ID zu erhalten.

Weitere Informationen zu verwaltetem und nicht verwaltetem Threading finden Sie in diesem MSDN-Artikel .

badbod99
quelle
4
Warum hat sich sonst niemand diese einfache Antwort ausgedacht?
Stefan Steinegger
2
Das funktioniert nicht. GetCurrentProcess (). Threads gibt eine ProcessThreadCollection zurück, die nicht in Threads konvertierbar ist. Ich sehe keine einfache Lösung.
Mafu
2
@ Mafutrct, aktualisierte Antwort. Diese Eigenschaft sollte wirklich .ProcessThreads heißen! Vielen Dank.
Badbod99
2
Empfehlen Sie, diesen Beitrag neu zu schreiben, um klarer zu machen, dass die beiden Thread-IDs unterschiedlich sind. Wenn jemand den letzten Satz nicht liest, steckt er einfach ManagedThreadId ein und versucht, ihn ProcessThread.Id zuzuordnen, wodurch Chaos entsteht.
ShadowChaser
1
Ich habe einen Link zu einem hilfreichen MSDN-Artikel hinzugefügt, der den Unterschied hervorhebt. Die Frage bezog sich jedoch auf das Abrufen der Thread-ID zum Debuggen (in diesem Fall die ManagedThreadID). Ich denke nicht, dass es nützlich ist, die Antwort mit Details zum Unterschied zwischen Betriebssystem und verwalteten Threads zu überladen.
Badbod99
46

Sie können das Veraltete verwenden AppDomain.GetCurrentThreadId, um die ID des aktuell ausgeführten Threads abzurufen. Diese Methode verwendet einen PInvoke für die Win32-API-Methode GetCurrentThreadIDund gibt die Windows-Thread-ID zurück.

Diese Methode wird als veraltet markiert, da das .NET-Thread-Objekt keinem einzelnen Windows-Thread entspricht und daher keine stabile ID vorhanden ist, die von Windows für einen bestimmten .NET-Thread zurückgegeben werden kann.

Weitere Gründe, warum dies der Fall ist, finden Sie in der Antwort des Konfigurators.

Paul Turner
quelle
VORSICHT Beachten Sie bei .Net Core 2.2, dass AppDomain.GetCurrentThreadId (über MethodInfo als veraltet aufgerufen) die verwaltete Thread-ID zurückgibt (unbrauchbar für den Abgleich von Process.GetCurrentProcess (). Threads-Sammlung.
Brewmanz
32

Um die Betriebssystem-ID zu erhalten, verwenden Sie:

AppDomain.GetCurrentThreadId()
Mark Byers
quelle
1
GetHashCode ist nicht unbedingt einzigartig! und sollte es nicht verwenden, um einen Thread zu identifizieren.
Dror Helper
2
Sie können AppDomain.GetCurrentThreadId () verwenden, wenn Sie die Betriebssystem-Thread-ID möchten, aber theoretisch können mehrere .NET-Threads denselben Betriebssystem-Thread verwenden. Thread.GetHashCode () gibt garantiert einen Wert zurück, der prozessweit eindeutig ist, was Sie wahrscheinlich wollen.
Mark Byers
3
Die Methode wird aus gutem Grund als veraltet markiert. Das ausführlichere Bild finden Sie in meiner Antwort und im Konfigurator.
Paul Turner
3
Nun, dies ist der einzige Weg, um zur OS-Thread-ID zu gelangen. Und dies sollte als die richtige Antwort markiert werden. Auch wenn ich mich nicht mehr darauf verlassen werde.
LolaRun
1
AppDomain.GetCurrentThreadId()ist veraltet: AppDomain.GetCurrentThreadId wurde veraltet, da es keine stabile ID liefert, wenn verwaltete Threads ausgeführt werden fibers (aka lightweight threads). Verwenden Sie die ManagedThreadIdEigenschaft on, um eine stabile Kennung für einen verwalteten Thread zu erhalten Thread. Verwendung:Thread.CurrentThread.ManagedThreadId
Lijo Joseph
22

Laut MSDN :

Eine ThreadId des Betriebssystems hat keine feste Beziehung zu einem verwalteten Thread, da ein nicht verwalteter Host die Beziehung zwischen verwalteten und nicht verwalteten Threads steuern kann. Insbesondere kann ein anspruchsvoller Host die CLR-Hosting-API verwenden, um viele verwaltete Threads für denselben Betriebssystem-Thread zu planen oder um einen verwalteten Thread zwischen verschiedenen Betriebssystem-Threads zu verschieben.

Im Grunde genommen entspricht das ThreadObjekt nicht unbedingt einem Betriebssystem-Thread - weshalb die native ID nicht verfügbar ist.

Konfigurator
quelle
Das Debug / Threads-Fenster in VS2010 zeigt "Managed Thread ID". Wie kann ich diesen bekommen?
Pavel Radzivilovsky
1
Verwenden Sie die ManagedThreadID-Eigenschaft msdn.microsoft.com/en-us/library/… . Dies ist jedoch nicht dasselbe wie die Betriebssystem-Thread-ID.
Konfigurator
15

Für diejenigen, die kurz vor dem Hack stehen:

    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;
    }
Ezolotko
quelle
für den Efford gestimmt, aber derzeit nicht funktioniert.
TakeMeAsAGuest
11

Um die aktuelle Thread-ID zu finden, verwenden Sie "Thread.CurrentThread.ManagedThreadId". In diesem Fall benötigen Sie möglicherweise die aktuelle Win32-Thread-ID. Verwenden Sie pInvoke, um diese Funktion abzurufen:

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

Zuerst müssen Sie die Verbindung zwischen verwaltetem Thread und Win32-Thread-ID speichern. Verwenden Sie ein Wörterbuch, das eine Win32-ID dem verwalteten Thread zuordnet.

Um dann einen Thread anhand seiner ID zu finden, iterieren Sie mit Process.GetCurrentProcess () über den Thread des Prozesses. Threads und suchen Sie den Thread mit dieser ID:

foreach (ProcessThread thread in Process.GetCurrentProcess().Threads)
{
     var managedThread = win32ToManagedThread[thread.id];
     if((managedThread.ManagedThreadId == threadId)
     {
         return managedThread;
     }
}
Dror Helfer
quelle
Ich glaube, das OP fragt nach der Betriebssystem-ID des Threads, die nicht mit der verwalteten Thread-ID übereinstimmt.
Brian Rasmussen
Dieser Code funktioniert nicht: Process.Threads eine Sammlung gibt ProcessThreadObjekte, das ist nicht das gleiche wie (noch tut es erben) Thread: (thread as Thread)eine Null - Referenz zurück.
Fredrik Mörk
Ich habe bemerkt, dass Code-Code ein paar Fehler hatte - behoben, versuchen Sie es jetzt
Dror Helper
1
Am Ende habe ich ein Wörterbuch verwendet, das eine Win32-ID einem verwalteten Thread zuordnet.
Contango
11

Der Offset unter Windows 10 ist 0x022C (x64-Bit-Anwendung) und 0x0160 (x32-Bit-Anwendung):

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;
}
Handball Reporter
quelle
1
Funktioniert auch unter Windows 7 x64 mit SP1. Nicht empfohlen. Nur für temporäre Tests verwenden.
Guan Boshen
5

System.Threading.Thread.CurrentThread.Name

System.Threading.Thread.CurrentThread.ManagedThreadId
Manu
quelle
5

Über verwalteten Code haben Sie Zugriff auf Instanzen des ThreadTyps für jeden verwalteten Thread. Threadkapselt das Konzept eines Betriebssystem-Threads und ab der aktuellen CLR gibt es eine Eins-zu-Eins-Entsprechung zu verwalteten Threads und Betriebssystem-Threads. Dies ist jedoch ein Implementierungsdetail, das sich in Zukunft ändern kann.

Die von Visual Studio angezeigte ID ist tatsächlich die Betriebssystem-Thread-ID. Dies entspricht nicht der ID des verwalteten Threads, wie in mehreren Antworten vorgeschlagen.

Der ThreadTyp enthält ein privates IntPtr-Mitgliedsfeld mit dem Namen DONT_USE_InternalThread, das auf die zugrunde liegende Betriebssystemstruktur verweist. Da dies jedoch wirklich ein Implementierungsdetail ist, ist es nicht ratsam, diese IMO fortzusetzen. Und der Name zeigt an, dass Sie sich nicht darauf verlassen sollten.

Brian Rasmussen
quelle
Um GetThreadId verwenden zu können, benötigen Sie das Handle, das Sie aus dem Feld DONT_USE erhalten.
Konfigurator
Ich weiß, aber wie gesagt, Sie können sich nicht wirklich darauf verlassen, dass verwaltete Threads direkt Betriebssystem-Threads zugeordnet werden, sodass ich nicht darauf zählen würde.
Brian Rasmussen
Vielen Dank für die Klarstellung und die Zusammenfassung des Problems. Wenn nun mehrere verwaltete Threads einem einzelnen Betriebssystem-Thread entsprechen können (wie der Konfigurator angegeben hat - und er bedankt sich), bedeutet dies, dass VS Betriebssystem-Threads und keine verwalteten Threads anzeigt.
LolaRun
@OhrmaZd: Ja, VS2005 / 2008 zeigt Betriebssystem-IDs für verwaltete Threads im Threads-Fenster an. VS2010B2 zeigt tatsächlich sowohl das Betriebssystem als auch die verwaltete ID pro Thread an.
Brian Rasmussen
@ Brian Rasmussen: Das ist eine Identifikation für einen verwalteten Thread! Vielen Dank für Ihr Wissen.
LolaRun
4

Sie können Thread.GetHashCode verwenden, der die verwaltete Thread-ID zurückgibt. Wenn Sie über den Zweck von GetHashCode nachdenken, ist dies sinnvoll - es muss eine eindeutige Kennung (z. B. Schlüssel in einem Wörterbuch) für das Objekt (den Thread) sein.

Die Referenzquelle für die Thread-Klasse ist hier aufschlussreich . (Zugegeben, eine bestimmte .NET-Implementierung basiert möglicherweise nicht auf diesem Quellcode, aber zu Debugging-Zwecken gehe ich mein Risiko ein.)

GetHashCode "stellt diesen Hash-Code für Algorithmen bereit, bei denen die Objektgleichheit schnell überprüft werden muss ". Daher eignet es sich gut zum Überprüfen der Thread-Gleichheit, um beispielsweise zu behaupten, dass eine bestimmte Methode auf dem Thread ausgeführt wird, von dem aus sie aufgerufen werden soll.

yoyo
quelle
4
Genial, ich hatte gerade diese 5 Jahre alte Frage für eine Stunde offen, kam zurück und sah "1 neue Antwort auf diese Frage": D
Ray
Diese Antwort wurde in einem anderen Kommentar angedeutet, aber ich habe sie nach einigen weiteren Nachforschungen verwendet. Möglicherweise nicht das, was das OP wollte. Wahrscheinlich ist es dem OP egal. Könnte für jemand anderen nützlich sein. (Und zumindest basierend auf der Referenzquelle kann dies der effizienteste Weg sein, um die Thread-ID zu erhalten.)
yoyo
Nun, ich bin gerade in einem anderen Bereich, aber damals hatten wir zwei IDs für einen Thread, die ID des nativen Threads und eine ID für den verwalteten Thread, und eine gehört zu einer anderen ... Grundsätzlich die IDs sollen die Threads identifizieren, GetHashCodes haben ein anderes Dienstprogramm und können kollidieren. Framework-Entwickler hätten keine ID implementiert, wenn wir GetHashCode
LolaRun
3
@ yoyo Kollisionen unterbrechen nicht die Verwendung des Wörterbuchs. Sie sind so konzipiert, dass sie eine geringe Kollisionswahrscheinlichkeit haben, überhaupt keine Kollision. Wenn Sie einen 128-Bit-Wert auf einen 64-Bit-Wert hashen, hat jeder Hash-Wert ungefähr 2 ^ 64 Kollisionen. Das Wörterbuch verfügt über einen Fallback-Algorithmus, wenn in dem seltenen Fall eine Kollision auftritt.
Bradgonesurfing
2
@bradgonesurfing Du hast absolut Recht und mein vorheriger Kommentar ist falsch. Die Leistung des Wörterbuchs wird durch Hash-Kollisionen beeinträchtigt, die Funktionalität bleibt jedoch korrekt. Ich entschuldige mich für den irreführenden Kommentar, danke, dass Sie darauf hingewiesen haben.
Jojo