Passo a passo: depurar um aplicativo paralelo no Visual Studio (C#, Visual Basic, C++)

Este passo a passo descreve como usar as janelas Tarefas Paralelas e Pilhas Paralelas para depurar um aplicativo paralelo. Essas janelas ajudam você a entender e verificar o comportamento em tempo de execução do código que usa a TPL (biblioteca de paralelismo de tarefas) ou o Runtime de Simultaneidade. Este passo a passo fornece código de exemplo que tem pontos de interrupção internos. Após a interrupção do código, este passo a passo mostra como usar as janelas Tarefas Paralelas e Pilhas Paralelas para examiná-lo.

Este passo a passo ensina estas tarefas:

  • Como exibir as chamadas de pilhas de todos os threads em uma exibição.

  • Como exibir a lista de instâncias de System.Threading.Tasks.Task que são criadas no seu aplicativo.

  • Como exibir as chamadas de pilhas de tarefas reais em vez de threads.

  • Como navegar até o código das janelas Tarefas Paralelas e Pilhas Paralelas.

  • Como as janelas lidam com a escala por agrupamento, zoom e outros recursos relacionados.

Pré-requisitos

Este passo a passo pressupõe que Apenas Meu Código está habilitado (ele está habilitado por padrão nas versões mais recentes do Visual Studio). No menu Ferramentas, clique em Opções, expanda o nó Depuração, selecione Geral e Habilitar Apenas Meu Código (somente Gerenciado). Se você não definir esse recurso, ainda poderá usar este passo a passo, mas os resultados poderão ser diferentes das ilustrações.

Exemplo de C#

Se você usar o exemplo do C#, este passo a passo também pressuporá que o código externo está oculto. Para ativar ou desativar a exibição do código externo, clique com o botão direito do mouse no cabeçalho de tabela Nome da janela Pilha de Chamadas e, depois, marque ou desmarque Mostrar Código Externo. Se você não definir esse recurso, ainda poderá usar este passo a passo, mas os resultados poderão ser diferentes das ilustrações.

Exemplo de C++

Se você usar o exemplo de C++, poderá ignorar referências ao Código Externo neste artigo. O código externo aplica-se somente ao exemplo do C#.

Ilustrações

As ilustrações neste artigo são gravadas em um computador quad core executando o exemplo de C#. Embora você possa usar outras configurações para concluir este passo a passo, as ilustrações poderão diferir do que é exibido em seu computador.

Criar o projeto de exemplo

O código de exemplo neste passo a passo é para um aplicativo que não faça nada. A finalidade do exercício é entender como usar as janelas de ferramentas para depurar um aplicativo paralelo.

  1. Abra o Visual Studio e crie um projeto.

    Se a janela inicial não estiver aberta, escolha Arquivo>Janela Inicial.

    Na tela Iniciar, selecione Novo projeto.

    Na tela Iniciar, selecione Criar um novo projeto.

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

    Depois de aplicar os filtros de linguagem de programação e plataforma, escolha o Aplicativo de Console para .NET Core ou C++ e, em seguida, clique em Avançar.

    Observação

    Caso não veja o modelo correto, vá em Ferramentas>Obter Ferramentas e Recursos..., 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 um nome ou use o nome padrão na caixa Nome do projeto. Depois, clique em Avançar ou Criar, qualquer uma dessas opções que estiver disponível.

    Para o .NET Core, escolha a estrutura de destino recomendada ou o .NET 8 e, em seguida, escolha Criar.

    Um novo projeto de console é exibido. Quando o projeto tiver sido criado, um arquivo de origem aparecerá.

  2. No projeto, abra o arquivo de código .cpp, .cs ou .vb. Exclua o conteúdo para criar um arquivo de código vazio.

  3. Cole o código a seguir para seu idioma escolhido no arquivo de código vazio.

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Diagnostics;
    
    class S
    {
      static void Main()
      {
        pcount = Environment.ProcessorCount;
        Console.WriteLine("Proc count = " + pcount);
        ThreadPool.SetMinThreads(4, -1);
        ThreadPool.SetMaxThreads(4, -1);
    
        t1 = new Task(A, 1);
        t2 = new Task(A, 2);
        t3 = new Task(A, 3);
        t4 = new Task(A, 4);
        Console.WriteLine("Starting t1 " + t1.Id.ToString());
        t1.Start();
        Console.WriteLine("Starting t2 " + t2.Id.ToString());
        t2.Start();
        Console.WriteLine("Starting t3 " + t3.Id.ToString());
        t3.Start();
        Console.WriteLine("Starting t4 " + t4.Id.ToString());
        t4.Start();
    
        Console.ReadLine();
      }
    
      static void A(object o)
      {
        B(o);
      }
      static void B(object o)
      {
        C(o);
      }
      static void C(object o)
      {
        int temp = (int)o;
    
        Interlocked.Increment(ref aa);
        while (aa < 4)
        {
          ;
        }
    
        if (temp == 1)
        {
          // BP1 - all tasks in C
          Debugger.Break();
          waitFor1 = false;
        }
        else
        {
          while (waitFor1)
          {
            ;
          }
        }
        switch (temp)
        {
          case 1:
            D(o);
            break;
          case 2:
            F(o);
            break;
          case 3:
          case 4:
            I(o);
            break;
          default:
            Debug.Assert(false, "fool");
            break;
        }
      }
      static void D(object o)
      {
        E(o);
      }
      static void E(object o)
      {
        // break here at the same time as H and K
        while (bb < 2)
        {
          ;
        }
        //BP2 - 1 in E, 2 in H, 3 in J, 4 in K
        Debugger.Break();
        Interlocked.Increment(ref bb);
    
        //after
        L(o);
      }
      static void F(object o)
      {
        G(o);
      }
      static void G(object o)
      {
        H(o);
      }
      static void H(object o)
      {
        // break here at the same time as E and K
        Interlocked.Increment(ref bb);
        Monitor.Enter(mylock);
        while (bb < 3)
        {
          ;
        }
        Monitor.Exit(mylock);
    
    
        //after
        L(o);
      }
      static void I(object o)
      {
        J(o);
      }
      static void J(object o)
      {
        int temp2 = (int)o;
    
        switch (temp2)
        {
          case 3:
            t4.Wait();
            break;
          case 4:
            K(o);
            break;
          default:
            Debug.Assert(false, "fool2");
            break;
        }
      }
      static void K(object o)
      {
        // break here at the same time as E and H
        Interlocked.Increment(ref bb);
        Monitor.Enter(mylock);
        while (bb < 3)
        {
          ;
        }
        Monitor.Exit(mylock);
    
    
        //after
        L(o);
      }
      static void L(object oo)
      {
        int temp3 = (int)oo;
    
        switch (temp3)
        {
          case 1:
            M(oo);
            break;
          case 2:
            N(oo);
            break;
          case 4:
            O(oo);
            break;
          default:
            Debug.Assert(false, "fool3");
            break;
        }
      }
      static void M(object o)
      {
        // breaks here at the same time as N and Q
        Interlocked.Increment(ref cc);
        while (cc < 3)
        {
          ;
        }
        //BP3 - 1 in M, 2 in N, 3 still in J, 4 in O, 5 in Q
        Debugger.Break();
        Interlocked.Increment(ref cc);
        while (true)
          Thread.Sleep(500); // for ever
      }
      static void N(object o)
      {
        // breaks here at the same time as M and Q
        Interlocked.Increment(ref cc);
        while (cc < 4)
        {
          ;
        }
        R(o);
      }
      static void O(object o)
      {
        Task t5 = Task.Factory.StartNew(P, TaskCreationOptions.AttachedToParent);
        t5.Wait();
        R(o);
      }
      static void P()
      {
        Console.WriteLine("t5 runs " + Task.CurrentId.ToString());
        Q();
      }
      static void Q()
      {
        // breaks here at the same time as N and M
        Interlocked.Increment(ref cc);
        while (cc < 4)
        {
          ;
        }
        // task 5 dies here freeing task 4 (its parent)
        Console.WriteLine("t5 dies " + Task.CurrentId.ToString());
        waitFor5 = false;
      }
      static void R(object o)
      {
        if ((int)o == 2)
        {
          //wait for task5 to die
          while (waitFor5) { ;}
    
    
          int i;
          //spin up all procs
          for (i = 0; i < pcount - 4; i++)
          {
            Task t = Task.Factory.StartNew(() => { while (true);});
            Console.WriteLine("Started task " + t.Id.ToString());
          }
    
          Task.Factory.StartNew(T, i + 1 + 5, TaskCreationOptions.AttachedToParent); //scheduled
          Task.Factory.StartNew(T, i + 2 + 5, TaskCreationOptions.AttachedToParent); //scheduled
          Task.Factory.StartNew(T, i + 3 + 5, TaskCreationOptions.AttachedToParent); //scheduled
          Task.Factory.StartNew(T, i + 4 + 5, TaskCreationOptions.AttachedToParent); //scheduled
          Task.Factory.StartNew(T, (i + 5 + 5).ToString(), TaskCreationOptions.AttachedToParent); //scheduled
    
          //BP4 - 1 in M, 2 in R, 3 in J, 4 in R, 5 died
          Debugger.Break();
        }
        else
        {
          Debug.Assert((int)o == 4);
          t3.Wait();
        }
      }
      static void T(object o)
      {
        Console.WriteLine("Scheduled run " + Task.CurrentId.ToString());
      }
      static Task t1, t2, t3, t4;
      static int aa = 0;
      static int bb = 0;
      static int cc = 0;
      static bool waitFor1 = true;
      static bool waitFor5 = true;
      static int pcount;
      static S mylock = new S();
    }
    

Depois de atualizar o arquivo de código, salve as alterações e compile a solução.

  1. No menu Arquivo, selecione Salvar Tudo.

  2. No menu Compilar, selecione Recompilar solução.

Observe que há quatro chamadas para Debugger.Break (DebugBreak no exemplo C++). Portanto, você não precisa inserir pontos de interrupção. Apenas a execução do aplicativo faz com que ele interrompa o depurador até quatro vezes.

Usar a janela Pilhas Paralelas: Exibição de Threads

Para começar, no menu Depurar, selecione Iniciar Depuração. Aguarde até que o primeiro ponto de interrupção seja atingido.

Exiba a pilha de chamadas de um único thread

  1. No menu Depurar, aponte para Janelas e clique em Threads. Encaixe a janela Threads na parte inferior do Visual Studio.

  2. No menu Depurar, aponte para Janelas e clique em Pilha de Chamadas. Encaixe a janela Pilha de Chamadas na parte inferior do Visual Studio.

  3. Clique duas vezes em um thread na janela Threads para torná-lo atual. Os threads atuais têm uma seta amarela. Quando você altera o thread atual, a pilha de chamadas é exibida na janela Pilha de Chamadas.

Examine a janela Pilhas Paralelas

No menu Depurar, aponte para Windows e clique em Pilhas Paralelas. Verifique se Threads está selecionado na caixa no canto superior esquerdo.

Usando a janela Pilhas Paralelas, você pode exibir várias pilhas de chamadas ao mesmo tempo em uma exibição. A ilustração a seguir mostra a janela Pilhas Paralelas acima da janela Pilha de Chamadas.

Screenshot of Threads view in Parallel Stacks window.

Threads view in Parallel Stacks window

A pilha de chamadas do thread principal é exibida em uma caixa e as pilhas de chamadas dos outros quatro threads são agrupadas em outra caixa. Quatro threads são agrupados porque os quadros de pilhas compartilham os mesmos contextos do método; isto é, estão nos mesmos métodos: A, B, e C. Para exibir as IDs de thread e os nomes dos threads que compartilham a mesma caixa, passe o mouse sobre o cabeçalho ([#] Threads). O thread atual é exibido em negrito.

Screenshot of Tooltip that shows thread IDs and names.

Tooltip that shows thread IDs and names

A seta amarela indica o quadro de pilhas do thread atual.

Você pode definir a quantidade de detalhes a ser mostrada para os quadros de pilha (Nomes de Módulo, Tipos de Parâmetro, Nomes de Parâmetro, Valores de Parâmetro, Números de Linha e Deslocamentos de Byte) clicando com o botão direito do mouse na janela Pilha de Chamadas.

Um realce azul ao redor de uma caixa indica que o thread atual é parte dessa caixa. O thread atual também é indicado pelo registro de ativação em negrito na dica de ferramenta. Se você clicar duas vezes no thread principal na janela Threads, poderá observar que o realce na janela Pilhas Paralelas se move adequadamente.

Screenshot of Highlighted main thread in Parallel Stacks window.

Highlighted main thread in Parallel Stacks window

Retome a execução até o segundo ponto de interrupção

Para retomar a execução até que o segundo ponto de interrupção seja atingido, no menu Depurar, selecione Continuar. A ilustração a seguir mostra a árvore do thread no segundo ponto de interrupção.

Screenshot of Parallel Stacks window that shows many branches.

Parallel Stacks window that shows many branches

No primeiro ponto de interrupção, quatro threads foram de S.A para S.B até o método S.C. Essas informações ainda estão visíveis na janela Pilhas Paralelas, mas os quatro threads progrediram mais. Um deles continuou até S.D e depois S.E. Outro continuou até S.F, S.G e S.H. Dois outros continuaram até S.I e S.J, e um deles foi até S.K e o outro continuou até o código externo de não usuário.

Você pode passar o mouse sobre registros de ativação para consultar as IDs de thread e outros detalhes do registro. O realce azul indica o thread atual e a seta amarela indica o registro de ativação ativo do thread atual.

Você pode passar o mouse sobre o cabeçalho da caixa, por exemplo, 1 Thread ou 2 Threads, para ver as IDs dos threads. Você pode passar o mouse sobre registros de ativação para consultar as IDs de thread e outros detalhes do registro. O realce azul indica o thread atual e a seta amarela indica o registro de ativação ativo do thread atual.

O ícone da trama de threads (sobrepondo linhas onduladas azuis e vermelhas) indica os registros de ativação ativos dos threads não atuais. Na janela Pilha de Chamadas, clique duas vezes em S.B para mudar de registros. A janela de Pilhas Paralelas indica o registro de ativação atual do thread atual usando um ícone de seta curva.

Observação

Para obter uma descrição de todos os ícones na janela de Pilhas Paralelas, confira Como usar a janela de Pilhas Paralelas.

Na janela Threads, alterne entre threads e observe que a exibição na janela Pilhas Paralelas é atualizada.

Você pode alternar para outro thread ou para outro registro de outro thread usando o menu de atalho na janela Pilhas Paralelas. Por exemplo, clique com o botão direito do mouse em S.J, aponte para Alternar para Quadro e clique em um comando.

Screenshot of Parallel Stacks Path of Execution.

Parallel Stacks Path of Execution

Clique com o botão direito do mouse em S.C e aponte para Alternar para Quadro. Um dos comandos tem uma marca de seleção que indica o registro de ativação do thread atual. Você pode alternar para esse quadro do mesmo thread (somente a seta curva se move) ou alternar para o outro thread (o realce azul também se move). A ilustração a seguir mostra o submenu.

Screenshot of Stacks menu with 2 options on C while J is current.

Stacks menu with 2 options on C while J is current

Quando um contexto de método estiver associado a apenas um registro de ativação, o cabeçalho da caixa exibirá 1 Thread e você poderá mudar para ele clicando duas vezes nele. Se você clicar duas vezes em um contexto de método que tenha mais de 1 registro associado a ele, o menu será exibido automaticamente. Enquanto você passa o mouse sobre os contextos de método, observe o triângulo preto no lado direito. Um clique nesse triângulo também exibe o menu de atalho.

Nos aplicativos grandes que possuem muitos threads, talvez seja conveniente se concentrar apenas em um subconjunto de threads. A janela Pilhas Paralelas pode exibir pilhas de chamadas apenas para threads sinalizados. Para sinalizar threads, use o menu de atalho ou a primeira célula de um thread.

Na barra de ferramentas, clique no botão Mostrar Somente Sinalizados ao lado da caixa de listagem.

Screenshot of Parallel Stacks window and tooltip.

Parallel Stacks window and tooltip

Agora, apenas threads sinalizados aparecem na janela de Pilhas Paralelas .

Retome a execução até o terceiro ponto de interrupção

  1. Para retomar a execução até que o terceiro ponto de interrupção seja atingido, no menu Depurar, selecione em Continuar.

    Quando vários threads estão no mesmo método, mas o método não estava no início da pilha de chamadas, o método é exibido em caixas diferentes. Um exemplo no ponto de interrupção atual é S.L, que tem três threads e aparece em três caixas. Clique duas vezes em S.L.

    Screenshot of Execution path in Parallel Stacks window.

    Execution path in Parallel Stacks window

    Observe que S.L está em negrito nas outras duas caixas para que você possa ver onde mais ele aparece. Se você quiser ver quais quadros chamam o S.L e quais quadros ele chama, selecione o botão Modo de Exibição do Método de Alternância na barra de ferramentas. A ilustração a seguir mostra a exibição do método da janela Pilhas Paralelas.

    Screenshot of Method view in Parallel Stacks window.

    Method view in Parallel Stacks window

    Observe como o diagrama girou no método selecionado e o posicionou em sua própria caixa no meio da exibição. Os receptores e os chamadores aparecem nas partes superior e inferior, respectivamente. Selecione o botão Modo de Exibição do Método de Alternância novamente para sair desse modo.

    O menu de atalho da janela Pilhas Paralelas também tem os seguintes itens.

    • Exibição hexadecimal alterna os números nas dicas de ferramenta entre decimais e hexadecimais.

    • As Configurações de Símbolo abrem as respectivas caixas de diálogo.

    • Mostrar Threads na Origem alterna a exibição de marcadores de thread no código-fonte, que mostra a localização dos threads no código-fonte.

    • Mostrar Código Externo exibe todos os quadros, mesmo que não estejam no código do usuário. Experimente para ver o diagrama se expandir para acomodar os outros quadros (que podem estar esmaecidos porque você não tem símbolos para eles).

  2. Na janela Pilhas Paralelas, verifique se a Autorrolagem para Quadro de Pilha Atual na barra de ferramentas está habilitada.

    Quando você tem diagramas grandes e avança para o próximo ponto de interrupção, talvez queira que a visualização role automaticamente até o quadro de pilha ativo do thread atual; isto é, o thread que atingiu o ponto de interrupção primeiro.

  3. Antes de continuar, na janela Pilhas Paralelas, role totalmente para a esquerda e para baixo.

Retome a execução até o quarto ponto de interrupção

  1. Para retomar a execução até que o quarto ponto de interrupção seja atingido, no menu Depurar, selecione Continuar.

    Observe como o modo de exibição rolou automaticamente até o local certo. Alterne os threads na janela Threads ou alterne registros de ativação na janela Pilha de Chamadas e observe como a exibição sempre rola automaticamente até o registro correto. Desabilite a opção Autorrolagem para Quadro de Ferramenta Atual e veja a diferença.

    A Exibição Panorâmica também é útil com diagramas grandes na janela Pilhas Paralelas. Por padrão, a Exibição Panorâmica está ativada. Você pode ativar/desativar a exibição panorâmica clicando no botão entre as barras de rolagem no canto inferior direito da janela, como mostra a ilustração a seguir.

    Screenshot of Birds eye view in Parallel Stacks window.

    Bird's-eye view in Parallel Stacks window

    Na exibição panorâmica, você pode mover o retângulo para percorrer rapidamente o diagrama.

    Outra maneira de mover o diagrama em qualquer direção é selecionar uma área em branco do diagrama e arrastá-lo para onde você quiser.

    Para ampliar ou reduzir o diagrama, mantenha pressionada a tecla CTRL enquanto move a roda do mouse. Como alternativa, selecione o botão Zoom na barra de ferramentas e use a ferramenta Zoom.

    Você também pode exibir as pilhas na direção de cima para baixo em vez de na direção de baixo para cima clicando no menu Ferramentas e em Opções e selecionando ou desmarcando a opção no nó Depuração.

  2. Antes de continuar, no menu Depurar, selecione Parar Depuração para encerrar a execução.

Usar a Janela Tarefas Paralelas e o Modo de Exibição Tarefas na janela Pilhas Paralelas

Recomendamos que você conclua os procedimentos anteriores antes de continuar.

Reinicie o aplicativo até que o primeiro ponto de interrupção seja atingido:

  1. No menu Depurar, selecione Iniciar Depuração e aguarde até que o primeiro ponto de interrupção seja atingido.

  2. No menu Depurar, aponte para Janelas e clique em Threads. Encaixe a janela Threads na parte inferior do Visual Studio.

  3. No menu Depurar, aponte para Windows e clique em Pilha de Chamadas. Encaixe a janela Pilha de Chamadas na parte inferior do Visual Studio.

  4. Clique duas vezes em um thread na janela Threads para torná-lo atual. Os threads atuais têm a seta amarela. Quando você altera o thread atual, as outras janelas são atualizadas. Em seguida, examinamos as tarefas.

  5. No menu Depurar, aponte para Windows e clique em Tarefas. A ilustração a seguir mostra a janela Tarefas.

    Screenshot of Four running tasks in Tasks window.

    Four running tasks in Tasks window

    Para cada tarefa em execução, você pode ler a ID, que é retornada pela propriedade do mesmo nome, a ID e o nome do thread que a executa, seu local (passar o mouse sobre ele exibe uma dica de ferramenta que tem a pilha de chamadas inteira). Além disso, na coluna Tarefa, você pode ver o método que foi passado na tarefa, em outras palavras, o ponto de partida.

    Você pode classificar qualquer coluna. Observe o glifo de classificação que indica a coluna e a direção de classificação. Também é possível reorganizar as colunas arrastando-as para a esquerda ou a direita.

    A seta amarela indica a tarefa atual. Você pode alternar tarefas clicando duas vezes em uma tarefa ou usando o menu de atalho. Quando você alterna tarefas, o thread subjacente se torna o atual e as outras janelas são atualizadas.

    Quando você alterna manualmente de uma tarefa para a outra, a estrutura de tópicos de seta indica o contexto atual do depurador para uma tarefa não recorrente.

    Quando você alterna manualmente de uma tarefa para outra, a seta amarela se move, mas uma seta branca ainda mostra a tarefa que causou a interrupção do depurador.

Retome a execução até o segundo ponto de interrupção

Para retomar a execução até que o segundo ponto de interrupção seja atingido, no menu Depurar, selecione Continuar.

Anteriormente, a coluna Status mostrava todas as tarefas como Ativas, mas agora duas tarefas estão Bloqueadas. As tarefas podem ser bloqueadas por diversos motivos diferentes. Na coluna Status, passe o mouse sobre uma tarefa em espera para saber por que ela está bloqueada. Por exemplo, na ilustração, a tarefa 11 está aguardando a tarefa 12.

Screenshot of Two waiting tasks in Tasks window.

Anteriormente, a coluna Status mostrava todas as tarefas como Ativas, mas agora duas tarefas estão Bloqueadas. As tarefas podem ser bloqueadas por diversos motivos diferentes. Na coluna Status, passe o mouse sobre uma tarefa em espera para saber por que ela está bloqueada. Por exemplo, na ilustração, a tarefa 4 está aguardando a tarefa 5.

Two waiting tasks in Tasks window

A tarefa 4, por sua vez, está aguardando um monitor de propriedade do thread atribuído à tarefa 2. (Clique com o botão direito do mouse na linha do cabeçalho e escolha Colunas>Atribuição de Thread para exibir o valor de atribuição de thread para a tarefa 2).

Waiting task and tooltip in Tasks window

Você pode sinalizar uma tarefa clicando no sinalizador na primeira coluna da janela Tarefas.

Você pode usar a sinalização para controlar tarefas entre pontos de interrupção diferentes na mesma sessão de depuração ou para filtrar as tarefas cujas pilhas de chamadas são mostradas na janela Pilhas Paralelas.

Quando você usou a janela Pilhas Paralelas antes, os threads do aplicativo foram exibidos. Exiba a janela Pilhas Paralelas novamente, mas desta vez exiba as tarefas do aplicativo. Faça isso selecionando Tarefas na caixa no canto superior esquerdo. A ilustração a seguir mostra o Modo de Exibição de Tarefas.

Screenshot of Tasks view in Parallel Stacks window.

Tasks view in Parallel Stacks window

Os threads que não estiverem executando tarefas no momento não serão mostrados na Exibição de Tarefas da janela Pilhas Paralelas. Além disso, nos threads que executam as tarefas, alguns registros de ativação que não são relevantes para as tarefas são filtrados das partes superior e inferior da pilha.

Exiba a janela Tarefas novamente. Clique com o botão direito do mouse em qualquer cabeçalho de coluna para ver um menu de atalho da coluna.

Você pode usar o menu de atalho para adicionar ou remover colunas. Por exemplo, a coluna AppDomain não está selecionada; consequentemente, não é exibida na lista. Selecione Pai. A coluna Pai aparece sem valores para as quatro tarefas.

Retome a execução até o terceiro ponto de interrupção

Para retomar a execução até que o terceiro ponto de interrupção seja atingido, no menu Depurar, selecione em Continuar.

Screenshot of Parent-child view in Tasks window.

Nesta execução de exemplo, observe que as tarefas 11 e 12 estão sendo executadas no mesmo thread (mostre a coluna de Atribuição de Thread se estiver oculta). Essas informações não são exibidas na janela Threads; sua exibição é outro benefício da janela Tarefas. Para confirmar isso, exiba a janela Pilhas Paralelas. Verifique se você está exibindo Tarefas. Você pode localizar as tarefas 11 e 12 examinando as dicas de ferramenta na janela de Pilhas Paralelas.

Task view in Parallel Stacks window

Uma nova tarefa, tarefa 5, está sendo executada e a tarefa 4 está aguardando. Veja o motivo passando o mouse sobre a tarefa em espera na janela Status. Na coluna Pai, observe que a tarefa 4 é o pai da tarefa 5.

Para visualizar melhor a relação pai-filho, clique com o botão direito do mouse na linha de cabeçalho da coluna e clique em Exibição Pai-Filho. Você verá a ilustração a seguir.

Parent-child view in Tasks window

Observe que as tarefas 4 e 5 estão em execução no mesmo thread (mostre a coluna Atribuição de Thread se ela estiver oculta). Essas informações não são exibidas na janela Threads; sua exibição é outro benefício da janela Tarefas. Para confirmar isso, exiba a janela Pilhas Paralelas. Verifique se você está exibindo Tarefas. Localize as tarefas 4 e 5 clicando duas vezes nelas na janela Tarefas. Quando você fizer isso, o realce azul na janela Pilhas Paralelas será atualizado. Você também pode localizar as tarefas 4 e 5 examinando as dicas de ferramenta na janela Pilhas Paralelas.

Task view in Parallel Stacks window

Na janela Pilhas Paralelas, clique com o botão direito do mouse em S.P e clique em Acessar o Thread. A janela alterna para o Modo de Exibição de Threads e o registro correspondente está na exibição. Você pode ver as duas tarefas no mesmo thread.

Highlighted thread in threads view

Esse é outro benefício da Exibição de Tarefas na janela Pilhas Paralelas, em comparação com a janela Threads.

Retome a execução até o quarto ponto de interrupção

Para retomar a execução até que o terceiro ponto de interrupção seja atingido, no menu Depurar, selecione em Continuar. Selecione o cabeçalho da coluna da ID para classificar por ID. Você verá a ilustração a seguir.

Screenshot of Four task states in Parallel Stacks window.

As tarefas 10 e 11 estão aguardando uma pela outra e estão bloqueadas. Há também várias novas tarefas que agora estão agendadas. As tarefas agendadas são aquelas iniciadas no código mas que ainda não foram executadas. Portanto, suas colunas de Localização e Atribuição de Thread mostram mensagens padrão ou estão vazias.

Four task states in Parallel Stacks window

Como a tarefa 5 foi concluída, ela não será mais exibida. Se esse não for o caso no seu computador e o deadlock não for mostrado, volte uma etapa clicando em F11.

As tarefas 3 e 4 estão aguardando uma pela outra e estão bloqueadas. Também há 5 novas tarefas que são filhos da tarefa 2 e estão agendadas agora. As tarefas agendadas são aquelas iniciadas no código mas que ainda não foram executadas. Portanto, as colunas Local e Atribuição de Thread estão vazias.

Exiba a janela Pilhas Paralelas novamente. O cabeçalho de cada caixa tem uma dica de ferramenta que mostra as IDs e os nomes de thread. Alterne para a Exibição de Tarefas na janela Pilhas Paralelas. Passe o mouse sobre um cabeçalho para ver o nome, a ID e o status da tarefa, como mostra a ilustração a seguir.

Header tooltip in Parallel Stacks window

Você pode agrupar as tarefas por coluna. Na janela Tarefas, clique com o botão direito do mouse no cabeçalho da coluna Status e clique em Agrupar por Status. A ilustração a seguir mostra a janela Tarefas agrupada por status.

Screenshot of Grouped tasks in Tasks window.

Grouped tasks in Tasks window

Também é possível agrupar por qualquer outra coluna. Agrupando tarefas, você pode se concentrar em um subconjunto de tarefas. Cada grupo recolhível tem uma contagem de itens que são agrupados.

O último recurso da janela Tarefas que deve ser examinado é o menu de atalho exibido quando você clica com o botão direito do mouse em uma tarefa.

O menu de atalho exibe comandos diferentes, dependendo do status da tarefa. Os comandos podem incluir Copiar, Selecionar Tudo, Exibição Hexadecimal, Alternar para Tarefas, Congelar o Thread Atribuído, Congelar Todos os Threads Menos Este, Descongelar o Thread Atribuído e Sinalizador.

Você pode congelar o thread subjacente de uma tarefa, ou tarefas, ou pode congelar todos os threads exceto o atribuído. Um thread congelado é representado na janela Tarefas como se apresenta na janela Threads, ao lado de um ícone azul de pausa.

Resumo

Este passo a passo demonstrou as janelas do depurador Tarefas Paralelas e Pilhas Paralelas. Use essas janelas em projetos reais que utilizam código multi-threaded. Você pode examinar o código paralelo escrito no C++, no C# ou no Visual Basic.