Отримання ідентифікатора потоку з потоку


319

У C # при налагодженні потоків, наприклад, ви можете побачити ідентифікатор кожного потоку.

Я не міг знайти спосіб програматизувати цю саму нитку. Я навіть не міг отримати ідентифікатор поточного потоку (у властивостях Thread.currentThread).

Отже, мені цікаво, як Visual Studio отримує ідентифікатори потоків, і чи є спосіб, наприклад, отримати ручку потоку з id 2345?

Відповіді:


437

GetThreadIdповертає ідентифікатор даної рідної нитки. Є способи змусити його працювати з керованими нитками, я впевнений, що все, що вам потрібно знайти, - це ручка нитки та передати її цій функції.

GetCurrentThreadId повертає ідентифікатор поточного потоку.

GetCurrentThreadIdзастаріло як .NET 2.0: рекомендованим способом є Thread.CurrentThread.ManagedThreadIdвластивість.


87
Оскільки я виявив це, набрав його, а потім йому сказали, що воно застаріле, поточний спосіб зробити це - Thread.CurrentThread.ManagedThreadId
Джеймс,

3
ManagedThreadId не є надійним підходом до визначення потоків, оскільки ідентифікатор властивості ManagedThreadId повторно використовується вашим додатком. Отже, це не є надійним ідентифікатором для потоків у деяких сценаріях, і ви відчуєте виняток: "Елемент з тим самим ключем уже додано." у рядку ... Надайте потоку унікальне ім'я під час його створення.
Forer

15
На цій посаді є кілька дуже поганих порад. Кілька людей рекомендують використовувати "ManagedThreadId" для ідентифікації потоку. Я відредагував публікацію, щоб видалити рекомендацію - на що мало хто вказав, що існують різні типи ідентифікаторів потоків. Ідентифіковані ідентифіковані потоки - це не те саме, що ідентифікатори некерованого потоку, і якщо люди повинні скопіювати та вставити цей код, можуть виникнути деякі дуже тонкі помилки синхронізації. Документація щодо MSDN для класу Thread дуже чітка щодо цього. Перегляньте зауваження на рівні класу.
ShadowChaser

3
Ви не синхронізуєте ідентифікатори, але ви використовуєте примітиви синхронізації, як мутекси. Це лише для налагодження.
Сліпий

11
Я хотів би опублікувати цей коментар, щоб помітити, що System.Threading.Thread.CurrentThread.ManagedThreadIdвін не працюватиме принаймні при використанні в SetWindowsHookEx. Натомість нам потрібно отримати ідентифікатор потоку з нативної функції win32 GetCurrentThreadId().
Король-король

82

У C # при налагодженні потоків, наприклад, ви можете побачити ідентифікатор кожного потоку.

Це будуть ідентифікатори керованих потоків. ManagedThreadIdє членом, Threadтому ви можете отримати Id від будь-якого об'єкта Thread . Це дозволить отримати поточний ManagedThreadID :

Thread.CurrentThread.ManagedThreadId

Щоб отримати потік ОС за його ідентифікатором потоку ОС (не ManagedThreadID) , ви можете спробувати трохи посилання.

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

Здається, немає ніякого способу перерахувати керовані потоки та немає зв’язку між ProcessThread та Thread, тому отримання керованої нитки за допомогою Id є складним.

Більш детальну інформацію про керовану та некеровану нарізку див. У цьому аркуші MSDN .


4
Чому ніхто інший не придумав такої простої відповіді?
Стефан Штайнгер

2
Це не працює. GetCurrentProcess (). Threads повертає ProcessThreadCollection, який не можна конвертувати в Threads. Я не бачу легкого виправлення.
мафу

2
@ mafutrct, оновлена ​​відповідь. Це властивість справді має бути названо .ProcessThreads! Дякую.
badbod99

2
Рекомендуйте цю публікацію переписати, щоб зрозуміти, що два ідентифікатори потоку різні. Якщо хтось не зможе прочитати останнє речення, він просто підключить ManagedThreadId і спробує зіставити його з ProcessThread.Id, створивши хавок.
ShadowChaser

1
Я додав посилання на корисну MSDN-статтю, що підкреслює різницю. Однак питання стосувалося отримання ідентифікатора потоку для налагодження (що в даному випадку є ManagedThreadID). Я не думаю, що захаращувати відповідь деталями різниці між ОС та керованими потоками корисно.
badbod99

46

Ви можете використовувати застарілий, AppDomain.GetCurrentThreadIdщоб отримати ідентифікатор поточного запущеного потоку. Цей метод використовує PInvoke для методу API Win32 GetCurrentThreadIDі поверне ідентифікатор потоку Windows.

Цей метод позначений як устарений, оскільки об’єкт .NET Thread не відповідає жодному потоку Windows, і як такий немає стабільного ідентифікатора, який може бути повернутий Windows для заданого потоку .NET.

Дивіться відповідь конфігуратора з додаткових причин, чому це так.


ОБЕРЕЖНО З .Net Core 2.2 зауважте, що AppDomain.GetCurrentThreadId (я посилався через MethodInfo як застарілий) повертає ідентифікований ідентифікатор потоку (непотрібний для відповідності Process.GetCurrentProcess (). Колекція ниток.
brewmanz

32

Щоб отримати ідентифікатор ОС:

AppDomain.GetCurrentThreadId()

1
GetHashCode не обов'язково унікальний! і не слід використовувати його для ідентифікації потоку.
Dror Helper

2
Ви можете використовувати AppDomain.GetCurrentThreadId (), якщо ви хочете ідентифікатор потоку ОС, але декілька потоків .NET теоретично могли б використовувати один і той же потік ОС. Thread.GetHashCode () гарантовано повертає значення, яке є унікальним для всіх процесів, а саме цього ви, мабуть, хочете.
Марк Байєрс

3
Метод позначається як застарілий, і з обґрунтованою причиною. Будь ласка, дивіться мою відповідь та конфігуратор для більш повної картини.
Пол Тернер

3
Ну це єдиний спосіб дістатися до ідентифікатора теми теми. І це слід позначити як правильну відповідь. Незважаючи на те, що я більше не буду на це покладатися.
LolaRun

1
AppDomain.GetCurrentThreadId()застаріла: AppDomain.GetCurrentThreadId застаріла, оскільки не забезпечує стабільний ідентифікатор, коли керовані потоки запущені fibers (aka lightweight threads). Щоб отримати стабільний ідентифікатор керованого потоку, використовуйте ManagedThreadIdвластивість увімкнено Thread. Використання:Thread.CurrentThread.ManagedThreadId
Лійо Йозеф

22

За даними MSDN :

Операційна система ThreadId не має фіксованого відношення до керованого потоку, оскільки некерований хост може керувати зв'язком між керованими та некерованими потоками. Зокрема, складний хост може використовувати API хостингу CLR для планування багатьох керованих потоків проти одного потоку операційної системи або переміщення керованого потоку між різними потоками операційної системи.

Таким чином, Threadоб'єкт не обов'язково відповідає потоці ОС - саме тому він не має відкритого ідентифікатора.


У вікні налагодження / потоків у VS2010 відображається "Ідентифікований ідентифікатор потоку". Як я можу отримати цю?
Павло Радзивиловський

1
Використовуйте ManagedThreadID властивість msdn.microsoft.com/en-us/library / ... . Це не те саме, що ідентифікатор потоку ОС.
конфігуратор

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 - використовуйте pInvoke, щоб отримати його з цією функцією:

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

Спочатку вам потрібно буде зберегти керований ідентифікатор потоку та з'єднання ідентифікатора нитки win32 - використовувати словник, який відображає ідентифікатор win32 для керованого потоку.

Потім, щоб знайти потік за його id iderate над потоком процесу, використовуючи Process.GetCurrentProcess (). Нитки та знайдіть нитку з цим id:

foreach (ProcessThread thread in Process.GetCurrentProcess().Threads)
{
     var managedThread = win32ToManagedThread[thread.id];
     if((managedThread.ManagedThreadId == threadId)
     {
         return managedThread;
     }
}

Я вважаю, що ОП запитує ідентифікатор OS потоку, який не є таким, як ідентифікований ідентифікатор потоку.
Брайан Расмуссен

Цей код не працює: Process.Threads повертає колекцію ProcessThreadоб'єктів, це не те саме, що (і не успадковується) Thread: (thread as Thread)поверне нульову посилання.
Fredrik Mörk

Я помітив, що в кодовому коді було кілька помилок - виправлено його спробуйте зараз
Dror Helper

1
Я в кінцевому підсумку використовував словник, який відображає id win32 в керовану нитку.
Контанго

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
Працює і в Windows 7 x64 із SP1. Не рекомендується, хоча. Використовувати лише у тимчасових випробуваннях.
guan boshen

5

System.Threading.Thread.CurrentThread.Name

System.Threading.Thread.CurrentThread.ManagedThreadId

5

З керованого коду ви маєте доступ до примірників Threadтипу для кожного керованого потоку. Threadінкапсулює поняття потоку ОС, а в поточному CLR є відповідність один на один керованим потокам та потокам ОС. Однак це деталізація щодо впровадження, яка може змінитися в майбутньому.

Ідентифікатор, показаний Visual Studio, є насправді ідентифікатором потоку ОС. Це не те саме, що ідентифікатор керованого потоку, як запропоновано кількома відповідями.

ThreadТипу дійсно включає приватне поле члена IntPtr під назвою DONT_USE_InternalThread, що вказує на основну структуру OS. Однак, оскільки це дійсно деталізація щодо впровадження, не доцільно продовжувати цей ІМО. І назва назви вказує на те, що на це не слід покладатися.


Для використання GetThreadId вам знадобиться ручка - яку ви отримуєте з поля DONT_USE.
конфігуратор

Я знаю, але, як я вже говорив, ви не можете по-справжньому розраховувати на те, що керовані потоки відображаються безпосередньо в потоках ОС, тому я б на це не розраховував.
Брайан Расмуссен

Дякую за роз’яснення та узагальнення проблеми. Але тепер, якщо кілька керованих потоків можуть відповідати одному потоку ОС (Як конфігуратор заявив - і він дякував), це означає, що VS показує потоки ОС, а не керовані нитки.
LolaRun

@OhrmaZd: Так, VS2005 / 2008 показує ідентифікатори ОС для керованих потоків у вікні "Нитки". VS2010B2 насправді показує ОС і керований ідентифікатор на один потік.
Брайан Расмуссен

@Brian Rasmussen: Тепер це ідентифікація керованої нитки! Дякуємо, що поділилися своїми знаннями.
LolaRun

4

Ви можете використовувати Thread.GetHashCode, який повертає ідентифікований ідентифікатор потоку. Якщо ви думаєте про призначення GetHashCode, це має добрий сенс - він повинен бути унікальним ідентифікатором (наприклад, ключем у словнику) для об'єкта (потоку).

Довідник для класу Thread повчально тут. (Звичайно, певна реалізація .NET може не базуватися на цьому вихідному коді, але для налагодження я буду ризикувати.)

GetHashCode "надає цей хеш-код для алгоритмів, які потребують швидкої перевірки рівності об'єктів", тому він добре підходить для перевірки рівності теми - наприклад, щоб стверджувати, що певний метод виконується на потоці, з якого ви хотіли його викликати.


4
Дивовижно, у мене щойно питання, яке було 5 років, відкрите на годину, повернувся і побачив "1 нову відповідь на це питання": D
Рей

Цю відповідь натякнули в іншому коментарі, але це було те, що я в кінцевому підсумку використав після деяких подальших досліджень. Можливо, не те, чого хотів ОП. Ймовірно, ОП вже не хвилює. Може бути корисним комусь іншому. (І, принаймні, виходячи з довідкового джерела, це може бути найефективнішим способом отримання ідентифікатора потоку.)
yoyo

ну я зараз в іншому полі, але тоді у нас було два ідентифікатори для потоку, ідентифікатор рідної нитки та ідентифікатор для керованого потоку, і один належить іншому ... В основному, Ідентифікатори призначені для ідентифікації потоків, GetHashCodes мають іншу корисність і можуть стикатися. Розробники рамок не реалізували б ідентифікатор, якби нам довелося використовувати GetHashCode
LolaRun

3
@yoyo Collisions не порушують використання словника. Вони розроблені так, щоб мати низьку ймовірність зіткнення, а не зіткнення взагалі. Якщо у вас є хеш-значення від 128 біт до значення 64 біт, то кожне хеш-значення матиме приблизно 2 ^ 64 зіткнення. Словник розроблений таким чином, щоб мати алгоритм резервного копіювання при зіткненні в рідкісному випадку.
bradgonesurfing

2
@bradgonesurfing Ви абсолютно праві, і мій попередній коментар неправильний. Продуктивність словника погіршиться при зіткненні хешів, але функціональність залишається правильною. Вибачте за оманливий коментар, дякую, що вказали на це.
yoyo
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.