Compartilhar via


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

O Visual Studio fornece várias ferramentas e elementos de interface do usuário para ajudar você a depurar aplicativos multithreaded. Este tutorial mostra como usar marcadores de thread, a janela Pilhas Paralelas, a janela Inspeçã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 depuração de aplicativos multithread.

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

A primeira etapa é criar um projeto de aplicativo multithreaded.

Criar um projeto de aplicativo multithreaded

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

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

    Na janela inicial, escolha Criar um novo projeto.

    Na janela Criar um novo projeto , insira ou digite o console na caixa de pesquisa. Em seguida, escolha C#, C++ou Visual Basic na lista de idiomas e escolha o Windows na lista plataforma.

    Depois de aplicar os filtros de linguagem e plataforma, escolha o modelo de 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..., o que abre o Instalador do Visual Studio. Escolha a carga de trabalho Desenvolvimento para desktop com .NET ou Desenvolvimento para desktop com C++ e, em seguida, selecione Modificar.

    Na janela Configurar seu novo projeto, digite ou insira MyThreadWalkthroughApp na caixa de 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 o .NET 8 e, em seguida, escolha Criar.

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

  2. Exclua o código que aparece no arquivo de origem e substitua-o pelo código atualizado a seguir. Escolha o snippet 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 Salvar Tudo.

  4. (Somente 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 Inicialização para Simples.

Depurar o aplicativo multithreaded

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

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

    Na sarjeta, um círculo vermelho indica que um ponto de interrupção 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 tópicos

  1. Na barra de ferramentas de depuração, selecione o botão Mostrar threads na origemMostrar Threads na Origem.

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

  3. Examine a medianiz no lado esquerdo da janela. Nessa linha, observe um ícone marcador de threadMarcador de Thread que se assemelha a dois threads retorcidos. O marcador de thread indica que um thread é interrompido neste local.

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

  4. Passe o ponteiro sobre o marcador de thread. O DataTip aparece, mostrando o nome e o número de ID do thread para cada thread interrompido. Nesse caso, o nome provavelmente é <noname>.

    Captura de tela da ID do Thread em um DataTip.

  5. Selecione o marcador de tópico para ver as opções disponíveis no menu de atalho.

Exibir as localizações do tópico

Na janela Pilhas Paralelas, você pode alternar entre o modo de exibição Threads e o modo de exibição Tarefas (para programação baseada em tarefas) e visualizar as informações de pilha de chamadas para cada thread. Neste aplicativo, podemos usar o modo de exibição Threads.

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

    Captura de tela da janela ParallelStacksWindow.

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

    • O thread atual (seta amarela) inseriu ServerClass.InstanceMethod. Você pode exibir a ID do thread e o quadro de pilha de um thread passando o mouse sobre ServerClass.InstanceMethod.
    • O thread 31724 está aguardando um bloqueio pertencente ao thread 20272.
    • O thread principal (lado esquerdo) parou em [código externo], que você pode exibir em detalhes se escolher Mostrar código externo.

    Janela de pilhas paralelas

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

    • O fio Principal no lado esquerdo foi interrompido em Thread.Start, onde o ponto de parada é identificado pelo ícone marcador de fio Thread Marker.
    • Dois threads entraram em ServerClass.InstanceMethod, sendo que um deles é o thread atual (seta amarela), enquanto o outro thread parou em Thread.Sleep.
    • Um novo thread (à direita) também está sendo iniciado, mas é interrompido em ThreadHelper.ThreadStart.

    Observação

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

  2. Para exibir os threads em vista de lista, selecione Depurar>Windows>Threads.

    Captura de tela da janela Threads.

    Nesse modo de exibição, você pode ver facilmente que o thread 20272 é o thread Principal e atualmente está localizado em código externo, especificamente System.Console.dll.

  3. Clique com o botão direito do mouse em 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 com o botão direito do mouse. Para este tutorial, você explorará mais desses detalhes na janela Inspeção Paralela (próximas seções).

Monitorar uma variável

  1. Abra a janela Inspeção Paralela selecionando Depurar>Windows>Inspeção Paralela>Inspeção Paralela 1.

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

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

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

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

    Janela de Inspeção Paralela

  4. Clique com o botão direito do mouse em uma das linhas na janela para ver as opções disponíveis.

Sinalizar e remover sinalizador de threads

Você pode marcar tópicos para acompanhar os tópicos importantes e ignorar os outros.

  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 Sinalizador.

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

  3. Na janela Inspeção Paralela , selecione o botão Mostrar Somente Threads SinalizadosMostrar Threads Sinalizados.

    Somente os threads sinalizados aparecem na lista.

    Dica

    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 no Cursor. Certifique-se de escolher os códigos que será atingidos por todos os threads sinalizados. 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 novamente para o modo Mostrar Todos os Threads .

  5. Para descomplagar threads, clique com o botão direito do mouse em um ou mais threads sinalizados na janela Inspeção Paralela e selecione Descomplagar.

Congelar e descongelar a execução do thread

Dica

Você pode congelar e descongelar (suspender e retomar) threads para controlar a ordem na qual os threads executam o trabalho. Isso pode ajudar você a resolver problemas de simultaneidade, como deadlocks e condições de corrida.

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

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

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

  3. Clique com o botão direito do mouse em uma linha e selecione Descongelar.

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

  4. Alterne para o editor de código e pressione F11. Somente o thread não descongelado é executado.

    O aplicativo também pode criar alguns novos threads. Os novos threads não são marcados e não são congelados.

Seguir um thread com pontos de interrupção condicionais

Pode ser útil seguir a execução de apenas um thread no depurador. Uma maneira de fazer isso é congelando threads nos quais 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 um thread sem congelar outros threads, você deve evitar invadir o código, exceto no thread no qual você está interessado. Você pode fazer essa tarefa definindo um ponto de interrupção condicional.

Você pode definir pontos de interrupção em condições diferentes, como o nome do thread ou a 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 um 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, insiradata == 5 para a expressão condicional.

    Ponto de interrupção condicionalPonto

    Dica

    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 as IDs de threads mudam quando você reinicia o depurador.

  3. Feche a janela Configurações do Ponto de Interrupção .

  4. Selecione o botão Reiniciar Aplicativo para reiniciar a sessão de depuração.

    Você divide o código no thread em que o valor da variável de dados é 5. Na janela Inspeção Paralela, procure a seta amarela que indica o contexto atual do depurador.

  5. Agora, você pode fazer step-over do código (F10) e step-in do código (F11) e seguir a execução do thread único.

    Desde que a condição do ponto de interrupção seja exclusiva da thread e o depurador não atinja nenhum outro ponto de interrupção em outras threads (talvez seja necessário desabilitá-los), você pode passar por cima do código e entrar no código sem alternar para outras threads.

    Observação

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