Partilhar via


Introdução à depuração de aplicativos multithreaded (C#, Visual Basic, C++)

Visual Studio fornece várias ferramentas e elementos de interface do usuário para ajudá-lo a depurar aplicativos multithreaded. Este tutorial mostra como usar marcadores de thread, a janela Pilhas Paralelas, a janela Visualização Paralela, pontos de interrupção condicionais e pontos de interrupção de filtro. A conclusão deste tutorial familiariza você com os recursos do Visual Studio para depurar aplicativos multithreaded.

Para tutoriais mais focados em cenários, consulte os artigos a seguir.

A primeira etapa é criar um projeto de aplicativo multithreaded.

Criar um projeto de aplicação multithreading

  1. Abra o Visual Studio e crie um novo projeto.

    Se a janela de início não estiver aberta, escolha Arquivo>Janela de Início.

    Na janela Iniciar, escolha Criar um novo projeto.

    Na janela Criar um novo projeto , digite ou digite console na caixa de pesquisa. Em seguida, escolha C#, C++ ou Visual Basic na lista Linguagem e, em seguida, escolha Windows na lista Plataforma .

    Depois de aplicar os filtros de linguagem e plataforma, escolha o modelo Aplicativo de Console para .NET ou C++ e escolha Avançar.

    Observação

    Se você não vir o modelo correto, vá para Ferramentas>Obter Ferramentas e Recursos..., que abre o Instalador do Visual Studio. Escolha o desenvolvimento de área de trabalho .NET ou Desenvolvimento de área de trabalho com C++ e, em seguida, escolha Modificar.

    Na janela Configurar seu novo projeto , digite ou digite MyThreadWalkthroughApp na caixa Nome do projeto . Em seguida, escolha Avançar ou Criar, qualquer opção disponível.

    Para um projeto .NET Core ou .NET 5+, escolha a estrutura de destino recomendada ou .NET 8 e, em seguida, escolha Criar.

    Um novo projeto de console é exibido. Depois que o projeto for criado, um arquivo de origem será exibido. Dependendo do idioma escolhido, o arquivo de origem pode ser chamado de Program.cs, MyThreadWalkthroughApp.cpp ou Module1.vb.

  2. Exclua o código que aparece no arquivo de origem e substitua-o pelo seguinte código atualizado. Escolha o trecho apropriado para sua configuração de código.

    using System;
    using System.Threading;
    
    public class ServerClass
    {
    
        static int count = 0;
        // The method that will be called when the thread is started.
        public void InstanceMethod()
        {
            Console.WriteLine(
                "ServerClass.InstanceMethod is running on another thread.");
    
            int data = count++;
            // Pause for a moment to provide a delay to make
            // threads more apparent.
            Thread.Sleep(3000);
            Console.WriteLine(
                "The instance method called by the worker thread has ended. " + data);
        }
    }
    
    public class Simple
    {
        public static void Main()
        {
            for (int i = 0; i < 10; i++)
            {
                CreateThreads();
            }
        }
        public static void CreateThreads()
        {
            ServerClass serverObject = new ServerClass();
    
            Thread InstanceCaller = new Thread(new ThreadStart(serverObject.InstanceMethod));
            // Start the thread.
            InstanceCaller.Start();
    
            Console.WriteLine("The Main() thread calls this after "
                + "starting the new InstanceCaller thread.");
    
        }
    }
    
  3. No menu Arquivo, selecione Guardar tudo.

  4. (Apenas Visual Basic) No Gerenciador de Soluções (painel direito), clique com o botão direito do mouse no nó do projeto, escolha Propriedades. Na guia Aplicativo , altere o objeto Startup para Simples.

Depurar o aplicativo multithreaded

  1. No editor de código-fonte, procure o seguinte trecho de código:

    Thread.Sleep(3000);
    Console.WriteLine();
    
  2. Clique com o botão esquerdo do mouse na margem esquerda da Thread.Sleep instrução ou, se for uma instrução C++, std::this_thread::sleep_for para inserir um novo ponto de interrupção.

    Na margem, um círculo vermelho indica que um ponto de paragem está definido neste local.

  3. No menu Depurar , selecione Iniciar Depuração (F5).

    O Visual Studio cria a solução, o aplicativo começa a ser executado com o depurador anexado e, em seguida, o aplicativo para no ponto de interrupção.

  4. No editor de código-fonte, localize a linha que contém o ponto de interrupção.

Descubra o marcador de rosca

  1. Na barra de ferramentas Depurar, selecione o botão Mostrar threads no SourceMostrar threads no Source.

  2. Pressione F11 duas vezes para avançar o depurador.

  3. Olhe para a calha do lado esquerdo da janela. Nessa linha, observe um ícone de marcador de thread Thread se assemelha a dois threads torcidos. O marcador de thread indica que um thread está parado neste local.

    Um marcador de thread pode ser parcialmente ocultado por um ponto de interrupção.

  4. Passe o ponteiro sobre o marcador de thread. Uma Dica de Dados é exibida informando o nome e o número de ID do thread para cada thread interrompido. Neste caso, o nome é provavelmente <noname>.

    Captura de ecrã do ID do thread numa dica de dados.

  5. Selecione o marcador de discussão para ver as opções disponíveis no menu de atalho.

Exibir os locais de thread

Na janela Pilhas Paralelas, pode alternar entre a visualização Threads e, para programação baseada em tarefas, a visualização Tarefas, e pode ver as informações da pilha de chamadas para cada thread. Neste aplicativo, podemos usar a visualização Threads.

  1. Abra a janela Pilhas Paralelas escolhendo Depurar>Windows>Pilhas Paralelas. Você deve ver algo semelhante ao seguinte. As informações exatas podem diferir dependendo da localização atual de cada thread, do hardware e da linguagem de programação.

    Captura de ecrã da janela Parallel Stacks.

    Neste exemplo, da esquerda para a direita, vemos estas informações para o código gerenciado:

    • O segmento atual (seta amarela) entrou em ServerClass.InstanceMethod. Você pode visualizar o ID do thread e o quadro da pilha de um thread passando o cursor do rato sobre ServerClass.InstanceMethod.
    • A tarefa 31724 está a aguardar num bloqueio pertencente à tarefa 20272.
    • O thread principal (lado esquerdo) parou em [Código externo], que você pode visualizar em detalhes se escolher Mostrar código externo.

    Janela de Pilhas Paralelas

    Neste exemplo, da esquerda para a direita, vemos estas informações para o código gerenciado:

    • O thread principal (lado esquerdo) parou no Thread.Start, onde o ponto de parada é identificado pelo ícone do marcador de thread Thread Marker.
    • Dois threads entraram no ServerClass.InstanceMethod, um dos quais é o thread atual (seta amarela), enquanto o outro thread parou em Thread.Sleep.
    • Um novo tópico (à direita) também está a começar, mas é interrompido em ThreadHelper.ThreadStart.

    Observação

    Para obter mais informações sobre como usar a vista Threads, consulte Depurar um deadlock usando a vista Threads.

  2. Para exibir os threads em um modo de exibição de lista, selecione Depurar>threads do>.

    Captura de ecrã da janela Threads.

    Nesta visualização, você pode ver facilmente que o thread 20272 é o thread principal e está atualmente localizado em código externo, especificamente System.Console.dll.

  3. Clique com o botão direito do mouse nas entradas na janela Pilhas Paralelas ou Threads para ver as opções disponíveis no menu de atalho.

    Você pode executar várias ações nesses menus do botão direito do mouse. Para este tutorial, você explora mais desses detalhes na janela Inspeção paralela (próximas seções).

Configurar uma monitorização numa variável

  1. Abra a janela Monitorização paralela selecionando Depurar> monitorizaçãoparalela>do Windows>Observação paralela 1.

  2. Selecione a célula onde vê o <Add Watch> texto (ou a célula de cabeçalho vazia na quarta coluna) e introduza data.

    Os valores da variável de dados para cada thread aparecem na janela.

  3. Selecione a célula onde vê o <Add Watch> texto (ou a célula de cabeçalho vazia na quinta coluna) e introduza count.

    Os valores da count variável para cada thread aparecem na janela. Se você ainda não vir tantas informações, tente pressionar F11 algumas vezes para avançar a execução dos threads no depurador.

    Janela de observação paralela

  4. Clique com o botão direito do rato numa das linhas da janela para ver as opções disponíveis.

Sinalizar e remover sinalização de tópicos

Você pode sinalizar threads para acompanhar threads importantes e ignorar os outros threads.

  1. Na janela Inspeção paralela , mantenha pressionada a tecla Shift e selecione várias linhas.

  2. Clique com o botão direito do mouse e selecione Sinalizar.

    Todos os threads selecionados são sinalizados. Agora, você pode filtrar para mostrar apenas threads sinalizados.

  3. Na janela Monitorização paralela, selecione o botão Mostrar apenas threads sinalizadosMostrar threadmarker de threads sinalizados.

    Apenas os threads sinalizados aparecem na lista.

    Sugestão

    Depois de sinalizar alguns threads, você pode clicar com o botão direito do mouse em uma linha de código no editor de código e escolher Executar threads sinalizados para cursor. Certifique-se de escolher o código que todos os threads sinalizados alcançarão. O Visual Studio pausará threads na linha de código selecionada, facilitando o controle da ordem de execução congelando e descongelando threads.

  4. Selecione o botão Mostrar somente threads sinalizados novamente para alternar de volta para o modo Mostrar todos os threads .

  5. Para desmarcar threads, clique com o botão direito do mouse em um ou mais threads sinalizados na janela Parallel Watch e selecione Unflag.

Congelar e descongelar a execução de threads

Sugestão

Você pode congelar e descongelar (suspender e retomar) threads para controlar a ordem em que os threads executam o trabalho. Isso pode ajudá-lo a resolver problemas de simultaneidade, como impasses e condições de disputa.

  1. Na janela Inspeção paralela, com todas as linhas selecionadas, clique com o botão direito do rato e selecione Congelar.

    Na segunda coluna, um ícone de pausa aparece para cada linha. O ícone de pausa indica que o fio está congelado.

  2. Desmarque todas as outras linhas selecionando apenas uma linha.

  3. Clique com o botão direito do rato numa linha e selecione Descongelar.

    O ícone de pausa desaparece nesta linha, o que indica que o thread não está mais congelado.

  4. Mude para o editor de código e pressione F11. Apenas o thread descongelado está em execução.

    O aplicativo também pode instanciar alguns novos threads. Todos os novos threads não são sinalizados e não são congelados.

Siga um único thread com pontos de interrupção condicionais

Pode ser útil seguir a execução de um único thread no depurador. Uma maneira de fazer isso é congelando fios que você não está interessado. Em alguns cenários, talvez seja necessário seguir um único thread sem congelar outros threads, por exemplo, para reproduzir um bug específico. Para seguir uma tarefa sem bloquear outras tarefas, deve-se evitar quebrar o código, exceto na tarefa em que se está interessado. Você pode fazer essa tarefa definindo um ponto de interrupção condicional.

Você pode definir pontos de interrupção em diferentes condições, como o nome do thread ou o ID do thread. Pode ser útil definir a condição em dados que você sabe que são exclusivos para cada thread. Essa abordagem é comum durante a depuração quando você está mais interessado em algum valor de dados específico do que em qualquer thread específico.

  1. Clique com o botão direito do mouse no ponto de interrupção criado anteriormente e selecione Condições.

  2. Na janela Configurações do ponto de interrupção , insira data == 5 a expressão condicional.

    Ponto de interrupção condicional

    Sugestão

    Se você estiver mais interessado em um thread específico, use um nome de thread ou ID de thread para a condição. Para fazer isso na janela Configurações do ponto de interrupção , selecione Filtrar em vez de Expressão condicional e siga as dicas de filtro. Talvez você queira nomear seus threads no código do aplicativo, pois os IDs dos threads mudam quando você reinicia o depurador.

  3. Feche a janela Configurações do ponto de interrupção .

  4. Selecione o botão Restart Restart App para reiniciar sua sessão de depuração.

    Você interrompe o código na linha de execução onde o valor da variável é 5. Na janela Observação paralela, procure a seta amarela indicando o contexto atual do depurador.

  5. Agora, é possível passar pelo código (F10) e entrar no código (F11) e seguir a execução de um único thread.

    Desde que a condição do ponto de interrupção seja exclusiva para o thread e o depurador não atinja outros pontos de interrupção em threads diferentes (pode ser necessário desativá-los), é possível avançar no código e entrar no código sem mudar para outros threads.

    Observação

    Quando você avança o depurador, todos os threads serão executados. No entanto, o depurador não quebrará o código em outros threads, a menos que um dos outros threads atinja um ponto de interrupção.