Получение идентификатора потока из потока

319

Например, в C # при отладке потоков вы можете видеть идентификатор каждого потока.

Я не мог найти способ получить ту же нить программным способом. Я даже не смог получить идентификатор текущего потока (в свойствах Thread.currentThread).

Итак, мне интересно, как Visual Studio получает идентификаторы потоков, и есть ли способ, например, получить дескриптор потока с идентификатором 2345?

LolaRun
источник

Ответы:

437

GetThreadIdвозвращает идентификатор данного нативного потока. Я уверен, что есть способы заставить его работать с управляемыми потоками, все, что вам нужно найти, - это дескриптор потока и передать его этой функции.

GetCurrentThreadId возвращает идентификатор текущего потока.

GetCurrentThreadIdустарел с .NET 2.0: рекомендуемый способ - это Thread.CurrentThread.ManagedThreadIdсвойство.

Blindy
источник
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так что вы можете получить идентификатор из любого объекта потока . Это даст вам текущий ManagedThreadID :

Thread.CurrentThread.ManagedThreadId

Чтобы получить поток ОС по его идентификатору потока ОС (не ManagedThreadID) , вы можете попробовать немного linq.

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

Кажется, что нет никакого способа перечислить управляемые потоки и нет никакой связи между ProcessThread и Thread, поэтому получить управляемый поток по его идентификатору довольно сложно.

Дополнительные сведения об управляемой и неуправляемой многопоточности см. В этой статье MSDN .

badbod99
источник
4
Почему никто не придумал этот простой ответ?
Стефан Штайнеггер
2
Это не работает. GetCurrentProcess (). Threads возвращает коллекцию ProcessThreadCollection, которая не конвертируется в Threads. Я не вижу легкого решения.
Мафу
2
@ mafutrct, обновленный ответ. Это свойство действительно должно называться .ProcessThreads! Спасибо.
badbod99
2
Рекомендую переписать этот пост, чтобы было яснее, что два идентификатора потока отличаются. Если кому-то не удается прочитать последнее предложение, он просто подключает ManagedThreadId и пытается сопоставить его с ProcessThread.Id, создавая havok.
ShadowChaser
1
Я добавил ссылку на полезную статью MSDN, подчеркивающую разницу. Однако вопрос был связан с получением идентификатора потока для отладки (в данном случае это ManagedThreadID). Я не думаю, что загромождение ответа деталями различия между ОС и управляемыми потоками полезно.
badbod99
46

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

Этот метод помечен как устаревший, поскольку объект потока .NET не соответствует ни одному потоку 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свойство on Thread. Использование:Thread.CurrentThread.ManagedThreadId
Lijo Joseph
22

По данным MSDN :

ThreadId операционной системы не имеет фиксированной связи с управляемым потоком, потому что неуправляемый хост может контролировать отношения между управляемыми и неуправляемыми потоками. В частности, сложный хост может использовать API-интерфейс хостинга CLR для планирования многих управляемых потоков в одном потоке операционной системы или для перемещения управляемого потока между различными потоками операционной системы.

Таким образом, по сути, Threadобъект не обязательно соответствует потоку ОС - вот почему у него нет собственного идентификатора.

конфигуратор
источник
Окно Debug / Threads в VS2010 показывает «ID управляемого потока». Как я могу получить это?
Павел Радзивиловский
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;
    }
ezolotko
источник
11

Чтобы найти текущий идентификатор потока, используйте - Thread.CurrentThread.ManagedThreadId. Но в этом случае вам может понадобиться текущий идентификатор потока win32 - используйте pInvoke, чтобы получить его с помощью этой функции:

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

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

Затем, чтобы найти поток по его идентификатору, выполните итерацию по потоку процесса с помощью Process.GetCurrentProcess (). Threads и найдите поток с этим идентификатором:

foreach (ProcessThread thread in Process.GetCurrentProcess().Threads)
{
     var managedThread = win32ToManagedThread[thread.id];
     if((managedThread.ManagedThreadId == threadId)
     {
         return managedThread;
     }
}
Dror Helper
источник
Я считаю, что OP запрашивает идентификатор ОС потока, который не совпадает с идентификатором управляемого потока.
Брайан Расмуссен
Этот код не работает: Process.Threads возвращает коллекцию ProcessThreadобъектов, это не то же самое, что (и не наследует) Thread: (thread as Thread)возвращает нулевую ссылку.
Фредрик Мёрк
Я заметил, что в коде было несколько ошибок - исправлено, попробуйте прямо сейчас
Dror Helper
1
Я закончил тем, что использовал словарь, который отображает идентификатор win32 в управляемый поток.
Contango
11

Смещение под Windows 10 составляет 0x022C (x64-bit-Application) и 0x0160 (x32-bit-Application):

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, а также. Не рекомендуется, хотя. Используйте только во временном тестировании.
Гуань Бошен
5

System.Threading.Thread.CurrentThread.Name

System.Threading.Thread.CurrentThread.ManagedThreadId
Manu
источник
5

Из управляемого кода у вас есть доступ к экземплярам Threadтипа для каждого управляемого потока. Threadинкапсулирует концепцию потока ОС, и в текущем CLR существует взаимно-однозначное соответствие с управляемыми потоками и потоками ОС. Однако это деталь реализации, которая может измениться в будущем.

ID, отображаемый в Visual Studio, фактически является идентификатором потока ОС. Это не то же самое, что идентификатор управляемого потока, как предлагается в нескольких ответах.

ThreadТипа действительно включает частное поле члена IntPtr под названием DONT_USE_InternalThread, что указывает на основную структуру OS. Тем не менее, поскольку это действительно детали реализации, не рекомендуется использовать эту IMO. И название указывает на то, что вы не должны полагаться на это.

Брайан Расмуссен
источник
Чтобы использовать GetThreadId, вам понадобится дескриптор, который вы получаете из поля DONT_USE.
Конфигуратор
Я знаю, но, как я уже сказал, вы не можете рассчитывать на тот факт, что управляемые потоки отображаются непосредственно на потоки ОС, поэтому я бы не стал рассчитывать на это.
Брайан Расмуссен
Большое спасибо за разъяснения и подведение итогов проблемы. Но теперь, если несколько управляемых потоков могут соответствовать одному потоку ОС (как сказал конфигуратор - и он поблагодарил), это означает, что VS показывает потоки ОС, а не управляемые потоки.
LolaRun
@OhrmaZd: Да, VS2005 / 2008 показывает идентификаторы ОС для управляемых потоков в окне потоков. VS2010B2 фактически показывает как ОС, так и управляемый идентификатор для каждого потока.
Брайан Расмуссен
@Brian Rasmussen: Теперь это идентификация для управляемого потока! Спасибо, что поделились своими знаниями.
LolaRun
4

Вы можете использовать Thread.GetHashCode, который возвращает идентификатор управляемого потока. Если вы думаете о назначении GetHashCode, это имеет смысл - это должен быть уникальный идентификатор (например, ключ в словаре) для объекта (потока).

Справочник для класса Thread поучительно здесь. (Конечно, конкретная реализация .NET может не основываться на этом исходном коде, но в целях отладки я рискну.)

GetHashCode «предоставляет этот хэш-код для алгоритмов, которые нуждаются в быстрой проверке равенства объектов», поэтому он хорошо подходит для проверки равенства потоков - например, чтобы утверждать, что конкретный метод выполняется в потоке, из которого вы хотели его вызвать.

йо Йо
источник
4
Круто, у меня только что был открыт этот 5-летний вопрос в течение часа, я вернулся и увидел «1 новый ответ на этот вопрос»: D
Ray
На этот ответ намекнули в другом комментарии, но это было то, что я в конечном итоге использовал после некоторых дальнейших исследований. Возможно, не то, что хотел ОП. Вероятно, ОП больше не волнует. Может быть полезным для кого-то еще. (И, по крайней мере, основываясь на справочном источнике, это может быть наиболее эффективным способом получения идентификатора потока.)
yoyo
ну, я сейчас нахожусь в другом поле, но тогда у нас было два идентификатора для потока, идентификатор родного потока и идентификатор для управляемого потока, и один принадлежит другому ... В принципе, Идентификаторы предназначены для идентификации потоков, GetHashCodes имеют другую утилиту и могут конфликтовать. Разработчики фреймворка не реализовали бы ID, если бы нам пришлось использовать GetHashCode
LolaRun
3
@yoyo Столкновения не нарушают использование словаря. Они разработаны так, чтобы иметь низкую вероятность столкновения, а не никакого столкновения вообще. Если вы хешируете 128-битное значение в 64-битное, то каждое хеш-значение будет иметь приблизительно 2 ^ 64 коллизий. В словаре предусмотрен запасной алгоритм, когда столкновение происходит в редком случае.
Bradgonesurfing
2
@bradgonesurfing Вы абсолютно правы, а мой предыдущий комментарий неверен. Производительность словаря будет ухудшаться из-за коллизий хешей, но функциональность остается правильной. Мои извинения за вводящий в заблуждение комментарий, спасибо за указание на это.
yoyo