Obtention de l'ID de thread à partir d'un thread

319

En C # lors du débogage de threads par exemple, vous pouvez voir l'ID de chaque thread.

Je n'ai pas pu trouver un moyen d'obtenir ce même fil, par programme. Je n'ai même pas pu obtenir l'ID du thread actuel (dans les propriétés du Thread.currentThread).

Donc, je me demande comment Visual Studio obtient les ID des threads, et est-il possible d'obtenir le handle du thread avec id 2345, par exemple?

LolaRun
la source

Réponses:

437

GetThreadIdrenvoie l'ID d'un thread natif donné. Il existe des moyens de le faire fonctionner avec des threads gérés, je suis sûr, tout ce que vous devez trouver est le handle de thread et le transmettre à cette fonction.

GetCurrentThreadId renvoie l'ID du thread actuel.

GetCurrentThreadIdest obsolète depuis .NET 2.0: la méthode recommandée est la Thread.CurrentThread.ManagedThreadIdpropriété.

Blindy
la source
87
Depuis que j'ai trouvé cela, l'ai tapé et qu'on m'a dit qu'il était obsolète, la façon actuelle de le faire est Thread.CurrentThread.ManagedThreadId
James
3
ManagedThreadId n'est pas une approche robuste pour identifier les threads lorsque l'ID de propriété ManagedThreadId est réutilisé par votre application. Ce n'est donc pas un identifiant fiable pour les threads dans certains scénarios et vous rencontrerez l'exception: "Un élément avec la même clé a déjà été ajouté." à la ligne ... Donnez au fil un nom unique lorsque vous le créez.
Forer
15
Il y a de très mauvais conseils sur ce post. Quelques personnes recommandent d'utiliser "ManagedThreadId" pour identifier un thread. J'ai édité le post pour supprimer la recommandation - ce que très peu ont souligné, c'est qu'il existe différents types d'ID de thread. Les ID de threads gérés ne sont pas la même chose que les ID de threads non gérés, et si les gens copiaient et collaient ce code, des bogues de synchronisation très subtils pourraient se produire. La documentation sur MSDN pour la classe Thread est très claire à ce sujet. Consultez les remarques au niveau de la classe.
ShadowChaser
3
Cependant, vous ne synchronisez pas sur les ID, vous utilisez des primitives de synchronisation comme les mutex. C'est uniquement à des fins de débogage.
Blindy
11
Je voudrais publier ce commentaire pour remarquer que System.Threading.Thread.CurrentThread.ManagedThreadIdcela ne fonctionnera pas au moins lors de l'utilisation dans un SetWindowsHookEx. Au lieu de cela, nous devons obtenir l'ID du thread à partir de la fonction native win32 GetCurrentThreadId().
King King
82

En C # lors du débogage de threads par exemple, vous pouvez voir l'ID de chaque thread.

Ce seront les identifiants des threads gérés. ManagedThreadIdest membre de Threadvous pouvez donc obtenir l'ID à partir de n'importe quel objet Thread . Cela vous donnera le ManagedThreadID actuel :

Thread.CurrentThread.ManagedThreadId

Pour obtenir un thread OS par son ID de thread OS (pas ManagedThreadID) , vous pouvez essayer un peu de linq.

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

Il semble qu'il n'y ait aucun moyen d'énumérer les threads gérés et aucune relation entre ProcessThread et Thread, donc obtenir un thread géré par son ID est difficile.

Pour plus de détails sur le filetage géré vs non géré, consultez cet article MSDN .

badbod99
la source
4
Pourquoi personne d'autre n'a trouvé cette réponse simple?
Stefan Steinegger
2
Cela ne fonctionne pas. GetCurrentProcess (). Threads renvoie un ProcessThreadCollection, qui n'est pas convertible en Threads. Je ne vois pas de solution facile.
mafu le
2
@ mafutrct, réponse mise à jour. Cette propriété devrait vraiment s'appeler .ProcessThreads! Merci.
badbod99
2
Recommandez que ce message soit réécrit pour qu'il soit plus clair que les deux identifiants de thread sont différents. Si quelqu'un ne parvient pas à lire la dernière phrase, il suffit de brancher ManagedThreadId et d'essayer de le mapper avec ProcessThread.Id, créant un havok.
ShadowChaser
1
J'ai ajouté un lien vers une acticule MSDN utile mettant en évidence la différence. Cependant, la question était liée à l'obtention de l'ID de thread pour le débogage (qui dans ce cas est le ManagedThreadID). Je ne pense pas que l'encombrement de la réponse avec des détails sur la différence entre le système d'exploitation et les threads gérés soit utile.
badbod99
46

Vous pouvez utiliser le obsolète AppDomain.GetCurrentThreadIdpour obtenir l'ID du thread en cours d'exécution. Cette méthode utilise un PInvoke à la méthode API Win32 GetCurrentThreadIDet renverra l'ID de thread Windows.

Cette méthode est marquée comme obsolète car l'objet .NET Thread ne correspond pas à un seul thread Windows, et en tant que tel, il n'y a pas d'ID stable qui peut être renvoyé par Windows pour un thread .NET donné.

Voir la réponse du configurateur pour plus de raisons pour lesquelles c'est le cas.

Paul Turner
la source
ATTENTION Avec .Net Core 2.2, notez que AppDomain.GetCurrentThreadId (que j'ai appelé via MethodInfo comme obsolète) renvoie l'ID de thread géré (inutile pour la correspondance avec Process.GetCurrentProcess (). Collection de threads.
brewmanz
32

Pour obtenir l'ID du système d'exploitation, utilisez:

AppDomain.GetCurrentThreadId()
Mark Byers
la source
1
GetHashCode n'est pas nécessairement unique! et ne doit pas l'utiliser pour identifier un thread.
Dror Helper
2
Vous pouvez utiliser AppDomain.GetCurrentThreadId () si vous voulez l'ID de thread OS, mais plusieurs threads .NET pourraient en théorie partager le même thread OS. Thread.GetHashCode () est garanti pour retourner une valeur unique à l'échelle du processus, ce que vous voulez probablement.
Mark Byers
3
La méthode est marquée comme obsolète et pour une bonne raison. Veuillez voir ma réponse et le configurateur pour une image plus complète.
Paul Turner
3
Eh bien, c'est le seul moyen d'accéder à l'ID de thread du système d'exploitation. Et cela devrait être marqué comme la bonne réponse. Même si je ne vais plus compter là-dessus.
LolaRun
1
AppDomain.GetCurrentThreadId()est obsolète: AppDomain.GetCurrentThreadId a été déconseillé car il ne fournit pas d'ID stable lorsque les threads gérés s'exécutent fibers (aka lightweight threads). Pour obtenir un identifiant stable pour un thread géré, utilisez la ManagedThreadIdpropriété on Thread. Utilisation:Thread.CurrentThread.ManagedThreadId
Lijo Joseph
22

Selon MSDN :

Un ThreadId du système d'exploitation n'a pas de relation fixe avec un thread géré, car un hôte non géré peut contrôler la relation entre les threads gérés et non gérés. Plus précisément, un hôte sophistiqué peut utiliser l'API d'hébergement CLR pour planifier de nombreux threads gérés par rapport au même thread du système d'exploitation, ou pour déplacer un thread géré entre différents threads du système d'exploitation.

Donc, fondamentalement, l' Threadobjet ne correspond pas nécessairement à un thread OS - c'est pourquoi il n'a pas l'ID natif exposé.

configurateur
la source
La fenêtre Débogage / Threads dans VS2010 affiche "ID de thread géré". Comment puis-je l'obtenir?
Pavel Radzivilovsky
1
Utilisez la propriété ManagedThreadID msdn.microsoft.com/en-us/library/… . Ce n'est pas la même chose que l'ID de thread du système d'exploitation.
configurateur
15

Pour ceux sur le point de pirater:

    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
la source
11

Pour trouver l'ID de thread actuel, utilisez - `Thread.CurrentThread.ManagedThreadId '. Mais dans ce cas, vous pourriez avoir besoin de l'ID de thread Win32 actuel - utilisez pInvoke pour l'obtenir avec cette fonction:

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

Vous devez d'abord enregistrer l'ID de thread géré et la connexion d'ID de thread win32 - utilisez un dictionnaire qui mappe un identifiant win32 sur le thread géré.

Ensuite, pour rechercher un thread par son id, parcourez le thread du processus à l'aide de Process.GetCurrentProcess (). Threads et recherchez le thread avec cet id:

foreach (ProcessThread thread in Process.GetCurrentProcess().Threads)
{
     var managedThread = win32ToManagedThread[thread.id];
     if((managedThread.ManagedThreadId == threadId)
     {
         return managedThread;
     }
}
Dror Helper
la source
Je crois que l'OP demande l'ID d'OS du thread, qui n'est pas le même que l'ID de thread managé.
Brian Rasmussen
Ce code ne fonctionne pas: Process.Threads retourne une collection d' ProcessThreadobjets, ce n'est pas la même chose que (ni n'hérite) Thread: (thread as Thread)retournera une référence nulle.
Fredrik Mörk
J'ai remarqué que le code du code avait quelques bugs - corrigé, essayez-le maintenant
Dror Helper
1
J'ai fini par utiliser un dictionnaire qui mappe un identifiant win32 à un thread géré.
Contango
11

Le décalage sous Windows 10 est 0x022C (application x64 bits) et 0x0160 (application x32 bits):

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;
}
Journaliste de handball
la source
1
Fonctionne également sur Windows 7 x64 avec SP1. Non recommandé cependant. Utiliser uniquement dans les tests temporaires.
guan boshen
5

System.Threading.Thread.CurrentThread.Name

System.Threading.Thread.CurrentThread.ManagedThreadId
Manu
la source
5

A partir du code managé, vous avez accès aux instances du Threadtype pour chaque thread managé. Threadencapsule le concept d'un thread OS et depuis le CLR actuel, il existe une correspondance biunivoque avec les threads gérés et les threads OS. Cependant, il s'agit d'un détail de mise en œuvre, qui pourrait changer à l'avenir.

L'ID affiché par Visual Studio est en fait l'ID de thread du système d'exploitation. Ce n'est pas la même chose que l'ID de thread géré comme suggéré par plusieurs réponses.

Le Threadtype inclut un champ membre IntPtr privé appelé DONT_USE_InternalThread, qui pointe vers la structure du système d'exploitation sous-jacent. Cependant, comme il s'agit vraiment d'un détail de mise en œuvre, il n'est pas conseillé de poursuivre cette OMI. Et le nom indique en quelque sorte que vous ne devriez pas vous fier à cela.

Brian Rasmussen
la source
Pour utiliser GetThreadId, vous auriez besoin du handle - que vous obtenez dans le champ DONT_USE.
configurateur
Je sais, mais comme je l'ai dit, vous ne pouvez pas vraiment compter sur le fait que les threads gérés mappent directement sur les threads du système d'exploitation, donc je ne compterais pas dessus.
Brian Rasmussen
Merci beaucoup pour la clarification et pour résumer le problème. Mais maintenant, si plusieurs threads gérés peuvent correspondre à un seul thread OS (comme le configurateur l'a déclaré - et il en est remercié), cela signifie que VS affiche les threads OS et non les threads gérés.
LolaRun
@OhrmaZd: Oui, VS2005 / 2008 affiche les ID de système d'exploitation pour les threads gérés dans la fenêtre Threads. VS2010B2 affiche en fait à la fois le système d'exploitation et l'ID géré par thread.
Brian Rasmussen
@Brian Rasmussen: Voilà une identification pour un thread géré! Merci de partager votre savoir.
LolaRun
4

Vous pouvez utiliser Thread.GetHashCode, qui renvoie l'ID de thread géré. Si vous pensez à l'objectif de GetHashCode, cela a du bon sens - il doit s'agir d'un identifiant unique (par exemple, la clé d'un dictionnaire) pour l'objet (le thread).

La source de référence pour la classe Thread est instructive ici. (Certes, une implémentation particulière de .NET peut ne pas être basée sur ce code source, mais à des fins de débogage, je vais tenter ma chance.)

GetHashCode "fournit ce code de hachage pour les algorithmes qui nécessitent des vérifications rapides de l'égalité des objets", il est donc bien adapté pour vérifier l'égalité des threads - par exemple pour affirmer qu'une méthode particulière s'exécute sur le thread à partir duquel vous souhaitez qu'elle soit appelée.

yo-yo
la source
4
Génial, je viens d'avoir cette question de 5 ans ouverte pendant une heure, je suis revenu et j'ai vu "1 nouvelle réponse à cette question": D
Ray
Cette réponse a été suggérée dans un autre commentaire, mais c'est ce que j'ai fini par utiliser après quelques recherches supplémentaires. Peut-être pas ce que le PO voulait. Il est probable que l'OP ne s'en soucie plus. Pourrait être utile à quelqu'un d'autre. (Et au moins sur la base de la source de référence, cela peut être le moyen le plus efficace pour obtenir l'ID de thread.)
yoyo
eh bien je suis dans un domaine différent en ce moment, mais à l'époque, nous avions deux identifiants pour un thread, l'id du thread natif et un identifiant pour le thread géré, et l'un appartient à un autre ... Principalement, le Les ID sont destinés à identifier les threads, GetHashCodes a un autre utilitaire et peut entrer en collision. Les développeurs de framework n'auraient pas implémenté un ID si nous devions utiliser GetHashCode
LolaRun
3
@yoyo Les collisions n'interrompent pas l'utilisation du dictionnaire. Ils sont conçus pour avoir une faible probabilité de collision, pas de collision du tout. Si vous hachez une valeur de 128 bits à une valeur de 64 bits, chaque valeur de hachage aura environ 2 ^ 64 collisions. Le dictionnaire est conçu pour avoir un algorithme de secours lorsqu'une collision se produit dans les rares cas.
bradgonesurfing
2
@bradgonesurfing Vous avez absolument raison, et mon commentaire précédent est faux. Les performances du dictionnaire se dégradent avec les collisions de hachage, mais la fonctionnalité reste correcte. Mes excuses pour le commentaire trompeur, merci de l'avoir signalé.
yoyo