Pobieranie identyfikatora wątku z wątku

319

Na przykład w języku C # podczas debugowania wątków można zobaczyć identyfikator każdego wątku.

Programowo nie mogłem znaleźć sposobu na uzyskanie tego samego wątku. Nie mogłem nawet uzyskać identyfikatora bieżącego wątku (we właściwościach Thread.currentThread).

Zastanawiam się więc, w jaki sposób Visual Studio pobiera identyfikatory wątków i czy istnieje sposób na uzyskanie uchwytu wątku o identyfikatorze 2345, na przykład?

LolaRun
źródło

Odpowiedzi:

437

GetThreadIdzwraca identyfikator danego natywnego wątku. Istnieją sposoby, aby działało z zarządzanymi wątkami, jestem pewien, wszystko, co musisz znaleźć, to uchwyt wątku i przekazać go do tej funkcji.

GetCurrentThreadId zwraca identyfikator bieżącego wątku.

GetCurrentThreadIdjest przestarzałe od .NET 2.0: zalecanym sposobem jest Thread.CurrentThread.ManagedThreadIdwłaściwość.

Blindy
źródło
87
Odkąd to znalazłem, napisałem na maszynie, a potem powiedziano mi, że jest przestarzałe, obecny sposób na zrobienie tego to Thread.CurrentThread.ManagedThreadId
James
3
ManagedThreadId nie jest niezawodnym podejściem do identyfikowania wątków, ponieważ identyfikator właściwości ManagedThreadId jest ponownie wykorzystywany przez aplikację. Dlatego nie jest to wiarygodny identyfikator wątków w niektórych scenariuszach, a wystąpi wyjątek: „Element z tym samym kluczem został już dodany”. na linii ... Nadaj wątkowi unikalną nazwę podczas jego tworzenia.
Forer
15
W tym poście jest kilka bardzo złych rad. Kilka osób zaleca użycie „ManagedThreadId” do identyfikacji wątku. Zredagowałem post, aby usunąć zalecenie - bardzo niewielu wskazało, że istnieją różne typy identyfikatorów wątków. Zarządzane identyfikatory wątków to nie to samo, co niezarządzane identyfikatory wątków, a jeśli ludzie skopiują i wkleją ten kod, mogą wystąpić bardzo subtelne błędy synchronizacji. Dokumentacja MSDN dla klasy Thread jest bardzo jasna na ten temat. Zobacz uwagi na poziomie klasy.
ShadowChaser 30.04.13
3
Nie synchronizujesz na identyfikatorach, używasz operacji podstawowych takich jak muteksy. Jest to tylko do celów debugowania.
Blindy,
11
Chciałbym opublikować ten komentarz, aby zauważyć, że System.Threading.Thread.CurrentThread.ManagedThreadIdnie zadziała przynajmniej w przypadku używania w SetWindowsHookEx. Zamiast tego musimy uzyskać identyfikator wątku z natywnej funkcji win32 GetCurrentThreadId().
King King
82

Na przykład w języku C # podczas debugowania wątków można zobaczyć identyfikator każdego wątku.

To będą identyfikatory zarządzanych wątków. ManagedThreadIdjest członkiem, Threaddzięki czemu możesz uzyskać identyfikator z dowolnego obiektu wątku . Otrzymasz bieżący identyfikator ManagedThreadID :

Thread.CurrentThread.ManagedThreadId

Aby uzyskać wątek systemu operacyjnego według jego identyfikatora wątku systemu operacyjnego (nie ManagedThreadID) , możesz wypróbować trochę linq.

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

Wydaje się, że nie ma sposobu na wyliczenie zarządzanych wątków i brak relacji między ProcessThread a Threadem, więc uzyskanie zarządzanego wątku na podstawie jego Id jest trudne.

Aby uzyskać więcej informacji na temat wątków zarządzanych i niezarządzanych, zobacz ten artykuł MSDN .

badbod99
źródło
4
Dlaczego nikt inny nie wymyślił tej prostej odpowiedzi?
Stefan Steinegger,
2
To nie działa. GetCurrentProcess (). Threads zwraca ProcessThreadCollection, którego nie można przekształcić w Threads. Nie widzę łatwego rozwiązania.
mafu
2
@ mafutrct, zaktualizowana odpowiedź. Tę właściwość należy naprawdę nazwać .ProcessThreads! Dzięki.
badbod99
2
Zaleca się przepisanie tego postu, aby wyjaśnić, że dwa identyfikatory wątków są różne. Jeśli ktoś nie przeczyta ostatniego zdania, po prostu podłączy ManagedThreadId i spróbuje zmapować go na ProcessThread.Id, tworząc spustoszenie.
ShadowChaser 30.04.13
1
Dodałem link do pomocnego zestawu MSDN podkreślającego różnicę. Pytanie dotyczyło jednak pobrania identyfikatora wątku do debugowania (w tym przypadku jest to ManagedThreadID). Nie uważam zaśmiecania odpowiedzi szczegółami różnicy między systemem operacyjnym a wątkami zarządzanymi jest przydatna.
badbod99
46

Możesz użyć przestarzałego, AppDomain.GetCurrentThreadIdaby uzyskać identyfikator aktualnie uruchomionego wątku. Ta metoda używa metody PInvoke do metody Win32 API GetCurrentThreadIDi zwraca identyfikator wątku systemu Windows.

Ta metoda jest oznaczona jako przestarzała, ponieważ obiekt wątku .NET nie odpowiada pojedynczemu wątkowi systemu Windows i jako taki nie ma stabilnego identyfikatora, który mógłby zostać zwrócony przez system Windows dla danego wątku .NET.

Zobacz odpowiedź konfiguratora, aby uzyskać więcej powodów, dla których tak jest.

Paul Turner
źródło
UWAGA W przypadku .Net Core 2.2 należy pamiętać, że AppDomain.GetCurrentThreadId (wywołany przez MethodInfo jako Przestarzały) zwraca identyfikator zarządzanego wątku (bezużyteczny do dopasowania Process.GetCurrentProcess (). Kolekcja wątków.
brewmanz
32

Aby uzyskać identyfikator systemu operacyjnego, użyj:

AppDomain.GetCurrentThreadId()
Mark Byers
źródło
1
GetHashCode niekoniecznie jest wyjątkowy! i nie powinien używać go do identyfikowania wątku.
Dror Helper,
2
Możesz użyć AppDomain.GetCurrentThreadId (), jeśli chcesz mieć identyfikator wątku systemu operacyjnego, ale wiele wątków .NET może teoretycznie współdzielić ten sam wątek systemu operacyjnego. Thread.GetHashCode () gwarantuje, że zwróci wartość, która jest unikalna dla całego procesu, a tego właśnie prawdopodobnie chcesz.
Mark Byers,
3
Metoda jest oznaczona jako przestarzała i nie bez powodu. Aby zobaczyć pełniejszy obraz, zobacz moją odpowiedź i konfigurator.
Paul Turner
3
Jest to jedyny sposób na uzyskanie identyfikatora wątku systemu operacyjnego. I to powinno być oznaczone jako poprawna odpowiedź. Mimo że nie będę już na tym polegać.
LolaRun
1
AppDomain.GetCurrentThreadId()jest przestarzały: AppDomain.GetCurrentThreadId został uznany za przestarzały, ponieważ nie zapewnia stabilnego identyfikatora podczas działania zarządzanych wątków fibers (aka lightweight threads). Aby uzyskać stabilny identyfikator zarządzanego wątku, użyj ManagedThreadIdwłaściwości na Thread. Zastosowanie:Thread.CurrentThread.ManagedThreadId
Lijo Joseph
22

Według MSDN :

Wątek systemu operacyjnego ThreadId nie ma stałej relacji z wątkiem zarządzanym, ponieważ niezarządzany host może kontrolować relacje między wątkami zarządzanymi i niezarządzanymi. W szczególności wyrafinowany host może używać CLR Hosting API do planowania wielu wątków zarządzanych względem tego samego wątku systemu operacyjnego lub do przenoszenia zarządzanego wątku między różnymi wątkami systemu operacyjnego.

Zasadniczo więc Threadobiekt niekoniecznie odpowiada wątkowi systemu operacyjnego - dlatego nie ma ujawnionego natywnego identyfikatora.

konfigurator
źródło
Okno debugowania / wątków w VS2010 pokazuje „Zarządzany identyfikator wątku”. Jak mogę to zdobyć?
Pavel Radzivilovsky
1
Użyj właściwości ManagedThreadID msdn.microsoft.com/en-us/library/… . Nie jest to jednak to samo, co identyfikator wątku systemu operacyjnego.
konfigurator
15

Dla tych, którzy zamierzają włamać się:

    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
źródło
11

Aby znaleźć bieżący identyfikator wątku, użyj - `Thread.CurrentThread.ManagedThreadId '. Ale w tym przypadku może być potrzebny bieżący identyfikator wątku win32 - użyj pInvoke, aby uzyskać go za pomocą tej funkcji:

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

Najpierw musisz zapisać identyfikator zarządzanego wątku i połączenie identyfikatora wątku win32 - użyj słownika odwzorowującego identyfikator win32 na zarządzany wątek.

Następnie, aby znaleźć wątek według jego id, iteruj go po wątku procesu za pomocą Process.GetCurrentProcess (). Wątki i znajdź wątek o tym identyfikatorze:

foreach (ProcessThread thread in Process.GetCurrentProcess().Threads)
{
     var managedThread = win32ToManagedThread[thread.id];
     if((managedThread.ManagedThreadId == threadId)
     {
         return managedThread;
     }
}
Dror Helper
źródło
Uważam, że OP prosi o identyfikator systemu operacyjnego wątku, który nie jest taki sam jak identyfikator zarządzanego wątku.
Brian Rasmussen
Ten kod nie działa: Process.Threads zwraca kolekcję ProcessThreadobiektów, to nie jest to samo co (ani nie dziedziczy) Thread: (thread as Thread)zwróci odwołanie zerowe.
Fredrik Mörk,
Zauważyłem, że w kodzie kodu było kilka błędów - naprawiłem go, spróbuj teraz
Dror Helper,
1
Skończyło się na tym, że użyłem słownika, który odwzorowuje identyfikator win32 na zarządzany wątek.
Contango,
11

Przesunięcie w systemie Windows 10 to 0x022C (aplikacja x64-bit) i 0x0160 (aplikacja x32-bit):

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;
}
Reporter piłki ręcznej
źródło
1
Działa również w systemie Windows 7 x64 z dodatkiem SP1. Nie polecam jednak. Używaj tylko w testach tymczasowych.
guan boshen,
5

System.Threading.Thread.CurrentThread.Name

System.Threading.Thread.CurrentThread.ManagedThreadId
Manu
źródło
5

Z kodu zarządzanego masz dostęp do instancji Threadtypu dla każdego zarządzanego wątku.Threadzawiera koncepcję wątku systemu operacyjnego, a od bieżącego CLR istnieje zgodność jeden-do-jednego z wątkami zarządzanymi i wątkami systemu operacyjnego. Jest to jednak szczegół implementacji, który może ulec zmianie w przyszłości.

Identyfikator wyświetlany przez Visual Studio jest w rzeczywistości identyfikatorem wątku systemu operacyjnego. To nie jest to samo, co identyfikator zarządzanego wątku, jak sugeruje kilka odpowiedzi.

Ten Threadtyp zawiera prywatne pole członka o nazwie IntPtr DONT_USE_InternalThread, które wskazuje na podstawową strukturę systemu operacyjnego. Ponieważ jest to tak naprawdę szczegół wdrożenia, nie jest wskazane, aby dążyć do tego IMO. A nazwa wskazuje, że nie powinieneś na tym polegać.

Brian Rasmussen
źródło
Aby użyć GetThreadId, potrzebujesz uchwytu - który otrzymujesz z pola DONT_USE.
konfigurator
Wiem, ale tak jak powiedziałem, tak naprawdę nie można liczyć na to, że zarządzane wątki mapują bezpośrednio na wątki systemu operacyjnego, więc nie liczyłem na to.
Brian Rasmussen
Bardzo dziękuję za wyjaśnienie i podsumowanie problemu. Ale teraz, jeśli wiele wątków zarządzanych może odpowiadać jednemu wątkowi systemu operacyjnego (jak stwierdził konfigurator - i podziękował), oznacza to, że VS pokazuje wątki systemu operacyjnego, a nie wątki zarządzane.
LolaRun,
@OhrmaZd: Tak, VS2005 / 2008 pokazuje identyfikatory systemu operacyjnego dla zarządzanych wątków w oknie Wątki. VS2010B2 faktycznie pokazuje zarówno system operacyjny, jak i identyfikator zarządzany dla każdego wątku.
Brian Rasmussen
@Brian Rasmussen: Teraz jest to identyfikator zarządzanego wątku! Dziękujemy za podzielenie się swoją wiedzą.
LolaRun,
4

Możesz użyć Thread.GetHashCode, który zwraca identyfikator zarządzanego wątku. Jeśli myślisz o celu GetHashCode, ma to sens - musi to być unikalny identyfikator (np. Klucz w słowniku) dla obiektu (wątku).

Źródło odniesienia dla klasy Thread jest pouczające tutaj. (Oczywiście, konkretna implementacja .NET może nie być oparta na tym kodzie źródłowym, ale dla celów debugowania zaryzykuję.)

GetHashCode „zapewnia ten kod skrótu dla algorytmów, które wymagają szybkiej kontroli równości obiektu”, więc dobrze nadaje się do sprawdzania równości wątków - na przykład w celu zapewnienia, że ​​określona metoda wykonuje się w wątku, z którego ma zostać wywołana.

yoyo
źródło
4
Wspaniale, właśnie otworzyłem to 5-letnie pytanie przez godzinę, wróciłem i zobaczyłem „1 nową odpowiedź na to pytanie”: D
Ray
Na tę odpowiedź zasugerowano w innym komentarzu, ale z tego skorzystałem po kilku dalszych badaniach. Być może nie to, czego chciał PO. Prawdopodobnie OP już się nie przejmuje. Może być przydatny dla kogoś innego. (I przynajmniej w oparciu o źródło referencyjne może to być najbardziej efektywny sposób na uzyskanie identyfikatora wątku.)
yoyo
cóż, jestem teraz w innym polu, ale wtedy mieliśmy dwa identyfikatory wątku, identyfikator wątku rodzimego i identyfikator zarządzanego wątku, i jeden należy do drugiego ... Zasadniczo Identyfikatory służą do identyfikacji wątków, kody GetHash mają inne narzędzie i mogą kolidować. Deweloperzy frameworków nie wdrożyliby identyfikatora, gdybyśmy musieli użyć GetHashCode
LolaRun
3
@yoyo Zderzenia nie przerywają używania słownika. Zostały zaprojektowane tak, aby mieć małe prawdopodobieństwo kolizji, a nie żadnej kolizji. Jeśli skrót ma wartość 128-bitową do wartości 64-bitowej, wówczas każda wartość skrótu spowoduje około 2 ^ 64 kolizji. Słownik został zaprojektowany w taki sposób, że ma algorytm awaryjny, gdy dochodzi do kolizji w rzadkich przypadkach.
bradgonesurfing
2
@bradgonesurfing Masz absolutną rację, a mój poprzedni komentarz jest błędny. Wydajność słownika ulegnie pogorszeniu wraz z kolizjami skrótu, ale funkcjonalność pozostanie poprawna. Przepraszam za wprowadzający w błąd komentarz, dziękuję za zwrócenie na to uwagi.
yoyo