Obtener la ID del hilo de un hilo

319

En C # al depurar hilos, por ejemplo, puede ver la identificación de cada hilo.

No pude encontrar una manera de obtener ese mismo hilo, programáticamente. Ni siquiera pude obtener la ID del hilo actual (en las propiedades de laThread.currentThread ).

Entonces, me pregunto cómo obtiene Visual Studio las ID de los subprocesos, y ¿hay alguna manera de obtener el identificador del subproceso con id 2345, por ejemplo?

LolaRun
fuente

Respuestas:

437

GetThreadIddevuelve la ID de un hilo nativo dado. Hay formas de hacerlo funcionar con hilos administrados, estoy seguro, todo lo que necesita encontrar es el identificador de hilo y pasarlo a esa función.

GetCurrentThreadId devuelve la ID del hilo actual.

GetCurrentThreadIdha quedado en desuso a partir de .NET 2.0: la forma recomendada es la Thread.CurrentThread.ManagedThreadIdpropiedad.

Ciego
fuente
87
Desde que encontré esto, lo escribí y luego me dijeron que estaba en desuso, la forma actual de hacerlo es Thread.CurrentThread.ManagedThreadId
James
3
ManagedThreadId no es un enfoque sólido para identificar subprocesos a medida que su aplicación reutiliza los ID de propiedad ManagedThreadId. Por lo tanto, no es un identificador confiable para subprocesos en algunos escenarios y experimentará la excepción: "Ya se ha agregado un elemento con la misma clave". en línea ... Déle al hilo un nombre único cuando lo cree.
Forer
15
Hay algunos consejos muy malos en esta publicación. Algunas personas recomiendan utilizar "ManagedThreadId" para identificar un hilo. Edité la publicación para eliminar la recomendación; lo que muy pocos han señalado es que hay diferentes tipos de identificadores de hilo. Las ID de subprocesos administrados no son lo mismo que los identificadores de subprocesos no administrados, y si la gente copiara y pegara ese código, podrían producirse algunos errores de sincronización muy sutiles. La documentación en MSDN para la clase Thread es muy clara al respecto. Ver los comentarios a nivel de clase.
ShadowChaser
3
Sin embargo, no se sincroniza en ID, usa primitivas de sincronización como mutexes. Esto es solo para fines de depuración.
Blindy
11
Me gustaría publicar este comentario para notar que System.Threading.Thread.CurrentThread.ManagedThreadIdno funcionará al menos cuando se usa en un SetWindowsHookEx. En su lugar, tenemos que obtener la identificación del hilo de la función nativa win32 GetCurrentThreadId().
Rey Rey
82

En C # al depurar hilos, por ejemplo, puede ver la identificación de cada hilo.

Este será el ID de los hilos gestionados. ManagedThreadIdes miembro de, Threadpor lo que puede obtener el ID de cualquier subproceso objeto . Esto le dará el ManagedThreadID actual :

Thread.CurrentThread.ManagedThreadId

Para obtener un subproceso del sistema operativo por su ID de subproceso del sistema operativo (no ManagedThreadID) , puede probar un poco de linq.

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

Parece que no hay forma de enumerar los subprocesos administrados y no hay relación entre ProcessThread y Thread, por lo que obtener un subproceso administrado por su Id es difícil.

Para obtener más detalles sobre subprocesos administrados y no administrados, consulte este artículo de MSDN .

badbod99
fuente
44
¿Por qué nadie más se le ocurrió esta respuesta simple?
Stefan Steinegger
2
Esto no funciona. GetCurrentProcess (). Threads devuelve ProcessThreadCollection, que no es convertible a Threads. No veo una solución fácil.
mafu
2
@ mafutrct, respuesta actualizada. Esa propiedad realmente debería llamarse .ProcessThreads! Gracias.
badbod99
2
Recomiende que se vuelva a escribir esta publicación para que quede más claro que los dos identificadores de hilo son diferentes. Si alguien no puede leer la última oración, simplemente conectará ManagedThreadId e intentará mapearlo contra ProcessThread.Id, creando havok.
ShadowChaser
1
He agregado un enlace a un útil artículo de MSDN que destaca la diferencia. Sin embargo, la pregunta estaba relacionada con la obtención de la ID del hilo para la depuración (que en este caso es ManagedThreadID). No creo que sea útil saturar la respuesta con detalles de la diferencia entre el sistema operativo y los hilos administrados.
badbod99
46

Puede usar el obsoleto AppDomain.GetCurrentThreadIdpara obtener la ID del subproceso que se está ejecutando actualmente. Este método utiliza un PInvoke para el método Win32 APIGetCurrentThreadID y devolverá el ID del hilo de Windows.

Este método está marcado como obsoleto porque el objeto .NET Thread no corresponde a un solo hilo de Windows y, como tal, no hay una identificación estable que pueda devolver Windows para un hilo .NET determinado.

Vea la respuesta del configurador para más razones por las cuales este es el caso.

Paul Turner
fuente
PRECAUCIÓN Con .Net Core 2.2, tenga en cuenta que AppDomain.GetCurrentThreadId (invoqué a través de MethodInfo como Obsoleto) devuelve el ID de hilo administrado (inútil para hacer coincidir Process.GetCurrentProcess (). Colección de hilos.
brewmanz
32

Para obtener el ID del sistema operativo, use:

AppDomain.GetCurrentThreadId()
Mark Byers
fuente
1
¡GetHashCode no es necesariamente único! y no debería usarlo para identificar un hilo.
Dror Helper
2
Puede usar AppDomain.GetCurrentThreadId () si desea la ID del subproceso del sistema operativo, pero múltiples subprocesos .NET podrían, en teoría, compartir el mismo subproceso del sistema operativo. Thread.GetHashCode () está garantizado para devolver un valor que es único en todo el proceso, que es lo que probablemente desee.
Mark Byers el
3
El método está marcado como obsoleto y con buenas razones. Consulte mi respuesta y los configuradores para obtener una imagen más completa.
Paul Turner el
3
Bueno, esta es la única forma de llegar a la ID del subproceso del sistema operativo. Y esto debe marcarse como la respuesta correcta. Aunque eso ya no voy a confiar en esto.
LolaRun el
1
AppDomain.GetCurrentThreadId()está obsoleto: AppDomain.GetCurrentThreadId ha quedado en desuso porque no proporciona un Id estable cuando se ejecutan subprocesos administrados fibers (aka lightweight threads). Para obtener un identificador estable para un hilo administrado, use la ManagedThreadIdpropiedad en Thread. Uso:Thread.CurrentThread.ManagedThreadId
Lijo Joseph
22

De acuerdo con MSDN :

Un ThreadId del sistema operativo no tiene una relación fija con un subproceso administrado, porque un host no administrado puede controlar la relación entre subprocesos administrados y no administrados. Específicamente, un host sofisticado puede usar la API CLR Hosting para programar muchos subprocesos administrados en el mismo subproceso del sistema operativo, o para mover un subproceso administrado entre diferentes subprocesos del sistema operativo.

Básicamente, el Threadobjeto no se corresponde necesariamente con un subproceso del sistema operativo, por lo que no tiene la identificación nativa expuesta.

configurador
fuente
La ventana Debug / Threads en VS2010 muestra "ID de hilo administrado". ¿Cómo puedo obtener este?
Pavel Radzivilovsky
1
Use la propiedad ManagedThreadID msdn.microsoft.com/en-us/library/… . Sin embargo, esto no es lo mismo que la ID del hilo del sistema operativo.
configurador
15

Para aquellos a punto de hackear:

    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
fuente
11

Para encontrar el Id. De hilo actual, use - `Thread.CurrentThread.ManagedThreadId '. Pero en este caso, es posible que necesite la identificación actual del subproceso win32: use pInvoke para obtenerlo con esta función:

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

Primero, deberá guardar la identificación del subproceso administrado y la conexión de la identificación del subproceso win32: use un diccionario que asigne una identificación de win32 al subproceso administrado.

Luego, para encontrar un subproceso por su id, itere sobre el subproceso del proceso usando Process.GetCurrentProcess (). Subprocesos y encuentre el subproceso con ese id:

foreach (ProcessThread thread in Process.GetCurrentProcess().Threads)
{
     var managedThread = win32ToManagedThread[thread.id];
     if((managedThread.ManagedThreadId == threadId)
     {
         return managedThread;
     }
}
Dror Helper
fuente
Creo que el OP está pidiendo la ID del sistema operativo del hilo, que no es lo mismo que la ID del hilo administrado.
Brian Rasmussen
Este código no funciona: Process.Threads devuelve una colección de ProcessThreadobjetos, esto no es lo mismo que (ni hereda) Thread: (thread as Thread)devolverá una referencia nula.
Fredrik Mörk el
Me di cuenta de que el código tenía algunos errores, lo solucioné, pruébelo ahora
Dror Helper el
1
Terminé usando un diccionario que asigna una identificación win32 a un hilo administrado.
Contango
11

El desplazamiento en Windows 10 es 0x022C (x64-bit-Application) y 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;
}
Reportero de balonmano
fuente
1
Funciona en Windows 7 x64 con SP1 también. Sin embargo, no se recomienda. Solo se usa en pruebas temporales.
guan boshen
5

System.Threading.Thread.CurrentThread.Name

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

Desde el código administrado tiene acceso a instancias del Threadtipo para cada subproceso administrado.Threadencapsula el concepto de un subproceso del sistema operativo y, a partir del CLR actual, existe una correspondencia uno a uno con subprocesos administrados y subprocesos del sistema operativo. Sin embargo, este es un detalle de implementación que puede cambiar en el futuro.

El ID que muestra Visual Studio es en realidad el ID del hilo del sistema operativo. Esto no es mismo que el ID de hilo administrado como lo sugieren varias respuestas.

El Threadtipo incluye un campo de miembro IntPtr privado llamado DONT_USE_InternalThread, que apunta a la estructura subyacente del sistema operativo. Sin embargo, como esto es realmente un detalle de implementación, no es aconsejable seguir esta OMI. Y el nombre indica que no debes confiar en esto.

Brian Rasmussen
fuente
Para usar GetThreadId necesitarías el identificador, que obtienes del campo DONT_USE.
Configurador
Lo sé, pero como dije, realmente no puedes contar con el hecho de que los hilos administrados se asignan directamente a los hilos del sistema operativo, por lo que no contaría con eso.
Brian Rasmussen
Muchas gracias por la aclaración y por resumir el problema. Pero ahora, si varios subprocesos administrados pueden corresponder a un solo subproceso del sistema operativo (como dijo el configurador, y se lo agradece), eso significa que VS está mostrando subprocesos del sistema operativo y no subprocesos administrados.
LolaRun el
@OhrmaZd: Sí, VS2005 / 2008 muestra ID del sistema operativo para subprocesos administrados en la ventana Subprocesos. VS2010B2 realmente muestra tanto el sistema operativo como la ID administrada por subproceso.
Brian Rasmussen
@Brian Rasmussen: ¡Ahora eso es una identificación para un hilo administrado! Gracias por compartir su conocimiento.
LolaRun el
4

Puede usar Thread.GetHashCode, que devuelve el ID del hilo administrado. Si piensa en el propósito de GetHashCode, esto tiene sentido: debe ser un identificador único (por ejemplo, clave en un diccionario) para el objeto (el hilo).

La fuente de referencia para la clase Thread es instructiva aquí. (De acuerdo, una implementación particular de .NET puede no estar basada en este código fuente, pero para propósitos de depuración, me arriesgaré).

GetHashCode "proporciona este código hash para algoritmos que necesitan comprobaciones rápidas de la igualdad de objetos", por lo que es muy adecuado para verificar la igualdad de subprocesos, por ejemplo, para afirmar que un método en particular se está ejecutando en el subproceso desde el que desea que se llame.

yoyó
fuente
44
Impresionante, tuve esta pregunta de 5 años abierta durante una hora, volví y vi "1 nueva respuesta a esta pregunta": D
Ray
Esta respuesta fue insinuada en otro comentario, pero fue lo que terminé usando después de una investigación adicional. Posiblemente no lo que el OP quería. Probablemente al OP ya no le importa. Puede ser útil para alguien más. (Y por lo menos sobre la base de la fuente de referencia, esto puede ser la forma más eficiente para obtener el ID del hilo.)
yoyo
bueno, estoy en un campo diferente en este momento, pero en aquel entonces, teníamos dos ID para un hilo, la identificación del hilo nativo, y una identificación para el hilo administrado, y una pertenece a otra ... Principalmente, el Los ID están destinados a identificar los hilos, GetHashCodes tienen otra utilidad y pueden colisionar. Los desarrolladores de framework no habrían implementado una ID si tuviéramos que usar GetHashCode
LolaRun
3
@yoyo Las colisiones no interrumpen el uso del diccionario. Están diseñados para tener una baja probabilidad de colisión, no ninguna colisión. Si hash un valor de 128 bits a un valor de 64 bits, cada valor de hash tendrá aproximadamente 2 ^ 64 colisiones. El diccionario está diseñado para tener un algoritmo alternativo cuando ocurre una colisión en el raro caso de que ocurra.
bradgonesurfing
2
@bradgonesurfing Tienes toda la razón, y mi comentario anterior es incorrecto. El rendimiento del diccionario se degradará con las colisiones hash, pero la funcionalidad sigue siendo correcta. Mis disculpas por el comentario engañoso, gracias por señalarlo.
yoyo