Obtendo o ID do Encadeamento de um Encadeamento

319

Em C # ao depurar threads, por exemplo, você pode ver a identificação de cada thread.

Não consegui encontrar uma maneira de obter o mesmo segmento, programaticamente. Eu não conseguia nem obter o ID do segmento atual (nas propriedades do Thread.currentThread).

Então, eu me pergunto como o Visual Studio obtém os IDs dos threads e existe uma maneira de obter o identificador do thread com a ID 2345, por exemplo?

LolaRun
fonte

Respostas:

437

GetThreadIdretorna o ID de um determinado segmento nativo. Existem maneiras de fazê-lo funcionar com threads gerenciados, tenho certeza, tudo que você precisa encontrar é o identificador de thread e passá-lo para essa função.

GetCurrentThreadId retorna o ID do encadeamento atual.

GetCurrentThreadIdfoi preterido no .NET 2.0: a maneira recomendada é a Thread.CurrentThread.ManagedThreadIdpropriedade

Blindy
fonte
87
Desde que eu encontrei isso, digitada, e, em seguida, foi dito que era Reprovado, a forma atual de fazer isso é Thread.CurrentThread.ManagedThreadId
James
3
ManagedThreadId não é uma abordagem robusta para identificar threads, pois os IDs de propriedade ManagedThreadId são reutilizados pelo seu aplicativo. Portanto, não é um identificador confiável para threads em alguns cenários e você experimentará a exceção: "Um item com a mesma chave já foi adicionado". na linha ... Atribua um nome exclusivo ao segmento quando você o criar.
Forer
15
Há alguns conselhos muito ruins por aí neste post. Algumas pessoas estão recomendando o uso de "ManagedThreadId" para identificar um thread. Editei o post para remover a recomendação - o que poucos apontaram é que existem diferentes tipos de IDs de threads. Os IDs de encadeamentos gerenciados não são a mesma coisa que os IDs de encadeamentos não gerenciados, e se as pessoas copiarem e colarem esse código, poderão ocorrer alguns bugs de sincronização muito sutis. A documentação no MSDN para a classe Thread é muito clara sobre isso. Veja as observações no nível da classe.
ShadowChaser
3
Você não sincroniza nos IDs, usa primitivas de sincronização como mutexes. Isso é apenas para fins de depuração.
Blindy
11
Eu gostaria de postar este comentário para perceber que System.Threading.Thread.CurrentThread.ManagedThreadIdnão funcionará pelo menos ao usar em um arquivo SetWindowsHookEx. Em vez disso, precisamos obter o ID do thread da função win32 nativa GetCurrentThreadId().
King King
82

Em C # ao depurar threads, por exemplo, você pode ver a identificação de cada thread.

Esses serão os IDs dos encadeamentos gerenciados. ManagedThreadIdé um membro Threadpara que você possa obter o ID de qualquer objeto Thread . Isso fornecerá o ManagedThreadID atual :

Thread.CurrentThread.ManagedThreadId

Para obter um encadeamento do SO com o ID do encadeamento do SO (não ManagedThreadID) , você pode tentar um pouco de linq.

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

Parece que não há como enumerar os threads gerenciados e nenhuma relação entre ProcessThread e Thread, portanto, obter um thread gerenciado por seu ID é difícil.

Para obter mais detalhes sobre o encadeamento gerenciado x não gerenciado, consulte este artigo do MSDN .

badbod99
fonte
4
Por que ninguém mais apresentou essa resposta simples?
precisa
2
Isso não funciona. GetCurrentProcess (). Threads retorna um ProcessThreadCollection, que não é conversível em Threads. Não vejo uma solução fácil.
Mafu
2
@ mafutrct, resposta atualizada. Essa propriedade deve realmente ser chamada de .ProcessThreads! Obrigado.
precisa saber é o seguinte
2
Recomenda que esta postagem seja reescrita para deixar mais claro que os dois IDs de encadeamento são diferentes. Se alguém não conseguir ler a última frase, apenas conectará ManagedThreadId e tentará mapeá-la contra ProcessThread.Id, causando estragos.
ShadowChaser
1
Adicionei um link para um artigo útil do MSDN destacando a diferença. No entanto, a questão estava relacionada à obtenção do ID do segmento para depuração (que nesse caso é o ManagedThreadID). Não acho que a resposta com detalhes da diferença entre SO e threads gerenciados seja útil.
badbod99
46

Você pode usar o obsoleto AppDomain.GetCurrentThreadIdpara obter o ID do encadeamento atualmente em execução. Este método usa um PInvoke para o método API Win32 GetCurrentThreadIDe retornará a identificação de thread do Windows.

Esse método está marcado como descontinuado porque o objeto .NET Thread não corresponde a um único thread do Windows e, como tal, não existe um ID estável que possa ser retornado pelo Windows para um determinado thread do .NET.

Consulte a resposta do configurador para obter mais razões pelas quais esse é o caso.

Paul Turner
fonte
CUIDADO Com .Net núcleo 2.2, nota que AppDomain.GetCurrentThreadId (I invocado via MethodInfo como obsoleto) retorna o ID thread gerenciado (inútil para combinar Process.GetCurrentProcess () Threads coleção..
brewmanz
32

Para obter o ID do sistema operacional, use:

AppDomain.GetCurrentThreadId()
Mark Byers
fonte
1
GetHashCode não é necessariamente único! e não deve usá-lo para identificar um segmento.
Dror Helper
2
Você pode usar AppDomain.GetCurrentThreadId () se desejar o ID do encadeamento do SO, mas vários encadeamentos .NET poderiam, em teoria, compartilhar o mesmo encadeamento do SO. É garantido que Thread.GetHashCode () retorne um valor exclusivo para todo o processo, que é o que você provavelmente deseja.
Mark Byers
3
O método está marcado como obsoleto e por boas razões. Consulte minha resposta e o configurador para obter uma imagem mais completa.
Paul Turner
3
Bem, esta é a única maneira de obter o ID do segmento do SO. E isso deve ser marcado como a resposta correta. Mesmo que eu não vou mais confiar nisso.
LolaRun 5/11/2009
1
AppDomain.GetCurrentThreadId()está obsoleto: AppDomain.GetCurrentThreadId foi descontinuado porque não fornece um ID estável quando os threads gerenciados estão em execução fibers (aka lightweight threads). Para obter um identificador estável para um segmento gerenciado, use a ManagedThreadIdpropriedade no Thread. Uso:Thread.CurrentThread.ManagedThreadId
Lijo Joseph
22

De acordo com o MSDN :

Um ThreadId do sistema operacional não possui um relacionamento fixo com um encadeamento gerenciado, porque um host não gerenciado pode controlar o relacionamento entre os encadeamentos gerenciados e não gerenciados. Especificamente, um host sofisticado pode usar a API de hospedagem do CLR para agendar muitos threads gerenciados no mesmo thread do sistema operacional ou para mover um thread gerenciado entre diferentes threads do sistema operacional.

Então, basicamente, o Threadobjeto não corresponde necessariamente a um encadeamento do SO - e é por isso que não tem o ID nativo exposto.

configurador
fonte
A janela Debug / Threads no VS2010 mostra "ID do thread gerenciado". Como posso obter este?
Pavel Radzivilovsky
1
Use a propriedade ManagedThreadID msdn.microsoft.com/en-us/library/… . Porém, isso não é o mesmo que o ID do encadeamento do SO.
Configurator
15

Para quem está prestes a invadir:

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

Para encontrar o ID do segmento atual, use - `Thread.CurrentThread.ManagedThreadId '. Mas, neste caso, você pode precisar do atual ID do thread do win32 - use o pInvoke para obtê-lo com esta função:

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

Primeiro, você precisará salvar a identificação do segmento gerenciado e a conexão do ID do segmento win32 - use um dicionário que mapeie um ID do win32 para o segmento gerenciado.

Em seguida, para localizar um segmento, ele identifica o segmento do processo usando Process.GetCurrentProcess (). Threads e encontra o segmento com esse ID:

foreach (ProcessThread thread in Process.GetCurrentProcess().Threads)
{
     var managedThread = win32ToManagedThread[thread.id];
     if((managedThread.ManagedThreadId == threadId)
     {
         return managedThread;
     }
}
Dror Helper
fonte
Acredito que o OP esteja solicitando o ID do sistema operacional do encadeamento, que não é o mesmo que o ID do encadeamento gerenciado.
Brian Rasmussen
Este código não funciona: Process.Threads retorna uma coleção de ProcessThreadobjetos, não é o mesmo que (nem herda) Thread: (thread as Thread)retornará uma referência nula.
Fredrik Mörk
Tenho notado que o código de código tinha alguns erro - fixa-lo experimentá-lo agora
Dror Helper
1
Acabei usando um dicionário que mapeia uma identificação win32 para um thread gerenciado.
Contango 20/08/2012
11

O deslocamento no Windows 10 é 0x022C (aplicativo de 64 bits) e 0x0160 (aplicativo de 32 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;
}
Repórter de Handebol
fonte
1
Funciona no Windows 7 x64 com SP1 também. Não é recomendado. Use apenas em testes temporários.
guan Boshen
5

System.Threading.Thread.CurrentThread.Name

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

No código gerenciado, você tem acesso a instâncias do Threadtipo para cada encadeamento gerenciado. Threadencapsula o conceito de um encadeamento do SO e, no CLR atual, há uma correspondência individual com encadeamentos gerenciados e encadeamentos do SO. No entanto, este é um detalhe de implementação, que pode mudar no futuro.

O ID exibido pelo Visual Studio é realmente o ID do encadeamento do SO. Esta é não o mesmo que o ID thread gerenciado como sugerido por várias respostas.

O Threadtipo inclui um campo de membro IntPtr particular chamado DONT_USE_InternalThread, que aponta para a estrutura do SO subjacente. No entanto, como esse é realmente um detalhe de implementação, não é aconselhável buscar essa OMI. E o nome indica que você não deve confiar nisso.

Brian Rasmussen
fonte
Para usar GetThreadId, você precisará do identificador - obtido no campo DONT_USE.
Configurator
Eu sei, mas como eu disse, você realmente não pode contar com o fato de que os threads gerenciados mapeiam diretamente para os threads do SO, portanto, eu não contaria com isso.
Brian Rasmussen
Muito obrigado pelo esclarecimento e por resumir o problema. Mas agora, se vários threads gerenciados podem corresponder a um único thread do SO (como o configurador declarou - e ele agradeceu), isso significa que o VS está mostrando threads do SO e não Threads Gerenciados.
LolaRun
@OhrmaZd: Sim, o VS2005 / 2008 mostra IDs de SO para threads gerenciados na janela Threads. O VS2010B2 realmente mostra o SO e o ID gerenciado por encadeamento.
Brian Rasmussen
@ Brian Rasmussen: Agora isso é uma identificação para um segmento gerenciado! Obrigado por compartilhar o seu conhecimento.
LolaRun 5/11
4

Você pode usar Thread.GetHashCode, que retorna o ID do thread gerenciado. Se você pensa no objetivo de GetHashCode, isso faz sentido - ele precisa ser um identificador exclusivo (por exemplo, chave em um dicionário) para o objeto (o encadeamento).

A fonte de referência para a classe Thread é instrutiva aqui. (Concedido, uma implementação .NET específica pode não se basear nesse código-fonte, mas para fins de depuração, arrisco-me.)

GetHashCode "fornece esse código de hash para algoritmos que precisam de verificações rápidas da igualdade de objetos", portanto, é adequado para verificar a igualdade de threads - por exemplo, para afirmar que um método específico está sendo executado no thread do qual você deseja que ele seja chamado.

yoyo
fonte
4
Incrível, eu só tive essa pergunta de 5 anos em aberto por uma hora, voltei e vi "1 nova resposta a esta pergunta": D
Ray
Essa resposta foi sugerida em outro comentário, mas foi o que acabei usando após algumas pesquisas. Possivelmente não o que o OP queria. Provavelmente o OP não se importa mais. Pode ser útil para outra pessoa. (E, pelo menos com base na fonte de referência, esta pode ser a maneira mais eficiente para obter o ID da thread.)
yoyo
bem, eu estou em um campo diferente agora, mas naquela época tínhamos dois IDs para um thread, o id do thread nativo e um id para o thread gerenciado, e um pertence a outro ... Principalmente, o Os IDs têm como objetivo identificar os threads, GetHashCodes tem outro utilitário e podem colidir. Desenvolvedores quadro não teria aplicado um ID se tivéssemos que usar GetHashCode
LolaRun
3
As colisões do @yoyo não quebram o uso do dicionário. Eles são projetados para ter uma baixa probabilidade de colisão, e nenhuma colisão. Se você hash um valor de 128 bits para um valor de 64 bits, todo valor de hash terá aproximadamente 2 ^ 64 colisões. O dicionário foi projetado para ter um algoritmo de fallback quando uma colisão ocorre nos raros casos em que ocorre.
Bradgonesurfing
2
@bradgonesurfing Você está absolutamente certo, e meu comentário anterior está errado. O desempenho do dicionário diminuirá com colisões de hash, mas a funcionalidade permanece correta. Peço desculpas pelo comentário enganador, obrigado por apontar isso.
yoyo